[Java]泛型(一)泛型类

news/2025/2/1 3:57:59 标签: java, 开发语言

 1. 什么是泛型类?

泛型类是指类中使用了占位符类型(类型参数)的类。通过使用泛型类,你可以编写可以处理多种数据类型的代码,而无需为每种类型编写单独的类。泛型类使得代码更具通用性和可重用性,同时可以保证类型安全。

2. 泛型类的基本语法

2.1 泛型类的定义通常包含:

  • 类型参数:通过尖括号< >指定,占位符通常是字母,比如TEKV等。
  • 类定义:类的成员变量、方法等可以使用类型参数。

2.2 泛型类的语法格式:

java">class ClassName<T> {  // T 是泛型类型参数
    // 类的成员变量
    private T value;

    // 泛型类的构造方法
    public ClassName(T value) {
        this.value = value;
    }

    // 泛型类的方法
    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

3. 如何使用泛型类

在你定义好泛型类之后,你可以在实例化时指定具体的类型,告诉泛型类使用什么类型。

3.1 示例:使用泛型类

java">class Box<T> {
    private T value;  // 存储泛型类型的数据

    // 构造方法
    public Box(T value) {
        this.value = value;
    }

    // 获取值的方法
    public T getValue() {
        return value;
    }

    // 设置值的方法
    public void setValue(T value) {
        this.value = value;
    }
}

public class generic_type {
    public static void main(String[] args) {
        // 创建一个泛型类的对象,指定类型为 String
        Box<String> stringBox = new Box<>("Hello");
        System.out.println(stringBox.getValue());  // 输出 "Hello"

        // 创建一个泛型类的对象,指定类型为 Integer
        Box<Integer> intBox = new Box<>(123);
        System.out.println(intBox.getValue());  // 输出 123

        // 创建一个泛型类的对象,指定类型为 Double
        Box<Double> doubleBox = new Box<>(45.67);
        System.out.println(doubleBox.getValue());  // 输出 45.67

        // 修改值
        stringBox.setValue("Goodbye");
        System.out.println(stringBox.getValue());  // 输出 "Goodbye"
    }
}

3.2 代码解释:

  1. Box<T>:这是一个泛型类,类名是 BoxT 是类型参数。T 可以代表任何数据类型,具体的类型将在创建对象时指定。

  2. private T valueBox 类中定义了一个成员变量 value,它的数据类型是泛型 T,这意味着它可以存储任何类型的数据。

  3. 构造方法 public Box(T value):构造方法接收一个类型为 T 的参数,表示初始化时将传入的数据赋值给 value

  4. getValue()setValue(T value):这两个方法分别用于获取和设置 value 的值。它们的类型都是 T,所以可以支持任意类型的数据。

3.3 在 Main 类中的应用:

  • Box<String> stringBox = new Box<>("Hello");:创建一个 Box 类型的对象,指定 TString 类型,初始化时给 value 赋值为 "Hello"
  • System.out.println(stringBox.getValue());:调用 getValue() 方法输出 "Hello"
  • Box<Integer> intBox = new Box<>(123);:创建一个 Box 类型的对象,指定 TInteger 类型,初始化时给 value 赋值为 123
  • Box<Double> doubleBox = new Box<>(45.67);:创建一个 Box 类型的对象,指定 TDouble 类型,初始化时给 value 赋值为 45.67

4.扩展:将Box作为内部类和外部类的区别

4.1 将Box作为外部类(如上示例的代码就是将Box作为外部类使用的)

4.2 将Box作为内部类使用

错误示例:

java">public class generic_type {
    class Box<T>{
        private T value;
        public Box(T value){
            this.value = value;
        }
        public T getValue(){
            return value;
        }
        public void setValue(T value){
            this.value = value;
        }
    }


        public static void main(String[] args){
            Box<String> stringBox = new Box<>("Hello");//报错
            System.out.println(stringBox.getValue());

            Box<Integer> intBox = new Box<>(123);//报错
            System.out.println(intBox.getValue());

            Box<Double> doubleBox = new Box<>(45.67);//报错
            System.out.println(doubleBox.getValue());

            stringBox.setValue("Goodbye!");
            System.out.println(stringBox.getValue());
        }


}

为什么会出现这样的错误: 如果 Box 类定义在 generic_type 类内部,它是一个 成员内部类Box 类只有在 generic_type 类的实例化对象中才有意义,且在外部无法直接使用 Box 类,必须通过 generic_type 的实例来访问它。

修改过后正确的内部类:

java">public class GenericType {
    class Box<T> {
        private T value;
        public Box(T value) {
            this.value = value;
        }
        public T getValue() {
            return value;
        }
        public void setValue(T value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        // 必须先创建外部类 GenericType 的对象
        GenericType genericType = new GenericType();
        GenericType.Box<String> stringBox = genericType.new Box<>("Hello");
        System.out.println(stringBox.getValue());
    }
}

4.3 使用内部类和外部类的区别:

  1. 外部类定义 Box<T>
    1. 独立性更强Box 类是独立的,可以单独作为一个类使用。比如,如果你在同一个包中的其他地方需要使用 Box 类,就不需要创建 generic_type 类的实例,可以直接创建 Box 的实例。
    2. 访问范围更广Box 类的作用域是整个包内或者是类所在的文件中,其他地方可以直接使用它。
  2. 内部类定义 Box<T>
    • 依赖于外部类:如果 Boxgeneric_type 类的成员(内部类),那么必须先创建 generic_type 类的对象,然后通过这个对象来创建 Box 的实例。
    • 访问范围较小Box 只能在 generic_type 类内部或通过 generic_type 的对象访问,限制了它的使用范围。其他类无法直接访问它,除非通过 generic_type 类实例。

5.泛型类的优点

  • 代码复用:通过泛型类,你不需要为每种数据类型编写不同的类,节省了代码量。
  • 类型安全:编译器可以在编译时检查类型是否匹配,避免了运行时错误和类型转换异常。
  • 灵活性:同一个类可以处理不同类型的数据,例如Box<String>Box<Integer>

6.泛型类的应用场景

6.1 应用场景

  • 集合类:Java的集合框架(如ListSetMap)广泛使用泛型类。你可以创建类型安全的集合。
  • 容器类:泛型类常用于实现容器类,比如将某个类型的元素封装在类中。
  • 工具类:很多通用的工具类,例如Java中的Collections类,也使用泛型来提高灵活性和类型安全。

6.2 示例:

6.2.1 使用泛型类实现一个集合类

java">// 泛型容器类 Container
public class Container<T> {
    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public static void main(String[] args) {
        // 使用 Container 存储 Integer 类型数据
        Container<Integer> intContainer = new Container<>();
        intContainer.setValue(42);
        System.out.println(intContainer.getValue());  // 输出 42

        // 使用 Container 存储 String 类型数据
        Container<String> strContainer = new Container<>();
        strContainer.setValue("Hello");
        System.out.println(strContainer.getValue());  // 输出 Hello
    }
}

6.2.2 使用泛型类实现一个容器类

java">// 泛型类:容器类
class Container<T> {
    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建容器实例,用String类型
        Container<String> stringContainer = new Container<>();
        stringContainer.setValue("Hello");
        System.out.println(stringContainer.getValue());  // 输出 Hello

        // 创建容器实例,用Integer类型
        Container<Integer> intContainer = new Container<>();
        intContainer.setValue(100);
        System.out.println(intContainer.getValue());  // 输出 100
    }
}

6.2.3 使用泛型实现一个工具类 

java">// 泛型工具类 GenericUtils
public class GenericUtils {
    
    // 泛型方法:交换数组中的两个元素
    public static <T> void swap(T[] array, int i, int j) {
        T temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    // 泛型方法:打印数组元素
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        // 使用泛型工具类处理 Integer 数组
        Integer[] intArray = {1, 2, 3, 4};
        swap(intArray, 0, 2);
        printArray(intArray);  // 输出:3 2 1 4

        // 使用泛型工具类处理 String 数组
        String[] strArray = {"apple", "banana", "cherry"};
        swap(strArray, 0, 1);
        printArray(strArray);  // 输出:banana apple cherry
    }
}

7. 泛型类的多个类型参数

7.1 解释

一个泛型类可以使用多个类型参数。例如,你可以定义一个包含键值对的容器类(如Map),它就需要两个类型参数,一个表示,一个表示

7.2 示例:使用多个类型参数

java">// 定义一个包含键值对的泛型类
class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建一个泛型Pair对象,指定键为String,值为Integer
        Pair<String, Integer> pair = new Pair<>("age", 25);
        System.out.println("Key: " + pair.getKey() + ", Value: " + pair.getValue());
    }
}

8.泛型类的继承

8.1 解释

泛型类的继承 是指在 Java 中通过继承泛型类来创建新的类。继承泛型类时,可以选择固定泛型类型或保留泛型类型占位符。具体的做法取决于是否希望在子类中指定一个具体的类型,或者希望子类保持泛型类型。

8.2 泛型类继承类型

8.2.1 子类继承泛型类并保留泛型类型

如果我们希望在子类中保持泛型类型(即让子类继续使用泛型),可以通过在子类中指定泛型类型。

示例:子类继承泛型类并保留泛型类型
java">// 泛型父类
class Box<T> {
    private T value;

    public Box(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

// 泛型子类,保留父类的泛型类型
class ColoredBox<T> extends Box<T> {
    private String color;

    public ColoredBox(T value, String color) {
        super(value);
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

public class Main {
    public static void main(String[] args) {
        // 使用 Integer 类型
        ColoredBox<Integer> intBox = new ColoredBox<>(123, "Red");
        System.out.println("Value: " + intBox.getValue() + ", Color: " + intBox.getColor());

        // 使用 String 类型
        ColoredBox<String> strBox = new ColoredBox<>("Hello", "Blue");
        System.out.println("Value: " + strBox.getValue() + ", Color: " + strBox.getColor());
    }
}
/*
输出:
Value: 123, Color: Red
Value: Hello, Color: Blue
*/
解释:
  • Box<T> 是一个泛型父类,表示一个可以存储任何类型 T 的盒子。
  • ColoredBox<T> 继承自 Box<T>,它保留了泛型 T,并增加了一个 color 属性。
  • main 方法中,我们通过创建 ColoredBox<Integer>ColoredBox<String> 来使用这个泛型子类,分别存储整数和字符串。

8.2.2 子类继承泛型类并指定具体的类型

有时你希望子类继承泛型类并指定一个具体的类型。这时,可以在继承时直接指定泛型类型,而不保留泛型类型参数。你可以在子类中明确指定泛型类型(通常是常见的基本类型或子类型)。

示例:子类继承泛型类并指定具体类型
java">// 泛型父类
class Box<T> {
    private T value;

    public Box(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

// 子类继承泛型父类,并指定具体的类型
class IntegerBox extends Box<Integer> {
    public IntegerBox(Integer value) {
        super(value);
    }

    public void printSquaredValue() {
        Integer value = getValue();
        System.out.println("Squared value: " + (value * value));
    }
}

public class Main {
    public static void main(String[] args) {
        // 使用 Integer 类型
        IntegerBox integerBox = new IntegerBox(5);
        System.out.println("Value: " + integerBox.getValue());
        integerBox.printSquaredValue();  // 输出:Squared value: 25
    }
}
/*
输出:
Value: 5
Squared value: 25
*/
解释:
  • Box<T> 是一个泛型父类,表示一个可以存储任何类型 T 的盒子。
  • IntegerBox 是一个子类,它继承了 Box<Integer>,并指定了 TInteger 类型。
  • IntegerBox 具有一个 printSquaredValue 方法,演示如何使用从父类继承的 getValue 方法。

8.2.3 子类继承泛型类并限制泛型的类型边界

你可以在子类中为泛型类型指定 边界(bounds),即限制泛型类型必须是某个特定类或接口的子类。例如,你可以让泛型类型 T 限定为 Number 或其子类。

示例:使用类型边界的泛型继承
java">// 泛型父类,限定 T 必须是 Number 的子类
class NumericBox<T extends Number> {
    private T value;

    public NumericBox(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public void printSquare() {
        System.out.println("Squared value: " + (value.doubleValue() * value.doubleValue()));
    }
}

// 子类继承泛型父类,并指定具体类型
class IntegerBox extends NumericBox<Integer> {
    public IntegerBox(Integer value) {
        super(value);
    }
}

class DoubleBox extends NumericBox<Double> {
    public DoubleBox(Double value) {
        super(value);
    }
}

public class Main {
    public static void main(String[] args) {
        // 使用 Integer 类型
        IntegerBox integerBox = new IntegerBox(5);
        integerBox.printSquare();  // 输出:Squared value: 25.0

        // 使用 Double 类型
        DoubleBox doubleBox = new DoubleBox(3.14);
        doubleBox.printSquare();  // 输出:Squared value: 9.8596
    }
}
/*
输出:
Squared value: 25.0
Squared value: 9.8596
*/
解释:
  • NumericBox<T extends Number> 是一个带有类型边界的泛型类,限定了 T 必须是 Number 或其子类。
  • IntegerBoxDoubleBox 分别继承 NumericBox<Integer>NumericBox<Double>,实现了不同类型的继承。
  • printSquare 方法中,调用了 value.doubleValue(),这在 Number 类中是定义好的,因此可以安全地对数值进行平方运算。

8.3 注意事项

  • 泛型类型的擦除: 泛型类型在编译时会进行类型擦除(type erasure),所以在运行时泛型类型会被替换为它的边界类型(例如 ObjectNumber)。这就是为什么泛型类不能直接创建数组。
  • 泛型和继承: 泛型类和子类之间的继承关系可以保留泛型类型,也可以指定具体的类型。你可以通过继承来扩展泛型类的功能,但要注意类型边界的使用。
  • 类型安全: 泛型的使用保证了类型安全,通过编译时的类型检查,避免了运行时类型错误,减少了类型强制转换的需要。

http://www.niftyadmin.cn/n/5838998.html

相关文章

基于 Redis GEO 实现条件分页查询用户附近的场馆列表

&#x1f3af; 本文档详细介绍了如何使用Redis GEO模块实现场馆位置的存储与查询&#xff0c;以支持“附近场馆”搜索功能。首先&#xff0c;通过微信小程序获取用户当前位置&#xff0c;并将该位置信息与场馆的经纬度数据一同存储至Redis中。利用Redis GEO高效的地理空间索引能…

goframe 多语言国际化解决方案

项目背景 本项目采用基于JSON配置的多语言国际化&#xff08;i18n&#xff09;解决方案&#xff0c;支持多种语言的无缝切换和本地化。 目录结构 manifest/ └── i18n/├── zh.json # 简体中文├── zh-tw.json # 繁体中文├── en.json # 英语├…

Vue3.0教程003:setup语法糖

文章目录 3.1 OptionsAPI与CompositionAPIOptions API的弊端Composition API的优势 3.2 拉开序幕的setup3.3 setup语法糖 3.1 OptionsAPI与CompositionAPI vue2的API设计是Options风格的vue3的API设计是Composition&#xff08;组合&#xff09;风格的 Options API的弊端 Opt…

【Python】深入探索Python元类:动态生成类与对象的艺术

《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 元类是Python中一个高级且强大的特性,允许开发者在类的创建过程中插入自定义逻辑,从而动态生成类和对象。本文将全面介绍Python中的元类概…

【漫话机器学习系列】070.汉明损失(Hamming Loss)

汉明损失&#xff08;Hamming Loss&#xff09; 汉明损失是多标签分类问题中的一种评价指标&#xff0c;用于衡量预测结果与实际标签之间的差异。它定义为预测错误的标签比例&#xff0c;即错误标签的个数占总标签数量的比值。 在多标签分类中&#xff0c;每个样本可以属于多…

基于springboot+vue的扶贫助农系统的设计与实现

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

在彼此的根系里呼吸

爱如草木&#xff0c;需以晨露滋养&#xff0c;而非绳索捆缚。一段健康的亲密关系&#xff0c;恰似两株根系相连却各自向阳的树——风起时枝叶相触&#xff0c;晴空下共享光影&#xff0c;却始终保有向地心深处生长的自由。那些纠缠的根须是信任编织的网&#xff0c;容得下沉默…

CNN的各种知识点(一):卷积神经网络CNN通道数的理解!

卷积神经网络CNN通道数的理解&#xff01; 通道数的核心概念解析1. 通道数的本质 2. 单张灰度图的处理示例&#xff1a; 3. 批量输入的处理通道与批次的关系&#xff1a; 4. RGB三通道输入的处理计算过程&#xff1a;示例&#xff1a; 5. 通道数的实际意义6. 可视化理解(1) 单通…