Skip to content

Java 泛型详解

Java 泛型(Generics)是一种强大的功能,主要用于 类型参数化。它允许你在定义类、接口和方法时使用类型参数,从而实现更强的类型检查和减少代码冗余。


1. 泛型的作用

  1. 提高代码的类型安全性:在编译时检测类型错误,避免运行时发生 ClassCastException
  2. 减少类型转换:不需要显式地进行类型转换。
  3. 提高代码的可读性和可维护性:泛型通过类型参数化,可以编写更通用的代码。

>[!注意] >泛型格式: <数据类型> >特点:只能支持引用数据类型


2. 泛型的基本语法

泛型类

定义类时,使用类型参数 <T>来限定变量item的数据类型 (T 是常用的命名,表示 Type):

java
public class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

特点

  • Box<String>Box<Integer> 是同一个泛型类,但在运行时,类型 StringInteger 被替代为具体的类型。

泛型方法

格式:修饰符 <泛型类型> 返回值类型 方法名(参数类型 变量名){} 定义方法时使用类型参数:

java
public class Util {
    // 泛型方法
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }
}

特点

  • 在方法名前声明类型参数 <T>,表示该方法是泛型方法。
  • 可以直接在方法中使用泛型类型。

泛型接口

格式修饰符 interface 接口名<泛型类型> {}

>[!使用方式] >1.实现类给出具体类型 class A implements<String> {} >2.实现类延续泛型 class A<T> implements<T> {}

定义接口时使用类型参数:

java
public interface Pair<K, V> {
    K getKey();
    V getValue();
}

// 实现泛型接口
class OrderedPair<K, V> implements Pair<K, V> {
    private K key;
    private V value;

    public OrderedPair(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 = new OrderedPair<>("Age", 25);
        System.out.println(pair.getKey() + ": " + pair.getValue());
    }
}

3. 泛型的通配符

通配符是泛型中的一个特殊语法,用于定义更加灵活的泛型结构。

无界通配符 <?>

表示任意类型,常用于不关心具体类型的场景:

java
public static void printList(List<?> list) {
    for (Object obj : list) {
        System.out.print(obj + " ");
    }
    System.out.println();
}

// 使用无界通配符
List<Integer> intList = Arrays.asList(1, 2, 3);
List<String> strList = Arrays.asList("A", "B", "C");

printList(intList); // 输出:1 2 3
printList(strList); // 输出:A B C

有界通配符

上界通配符 <? extends T>

表示该类型是 TT 的子类型:

java
public static void sum(List<? extends Number> list) {
    double total = 0;
    for (Number num : list) {
        total += num.doubleValue();
    }
    System.out.println("Sum: " + total);
}

// 使用上界通配符
List<Integer> intList = Arrays.asList(1, 2, 3);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);

sum(intList);    // 输出:Sum: 6.0
sum(doubleList); // 输出:Sum: 6.6

下界通配符 <? super T>

表示该类型是 TT 的父类型:

java
public static void addIntegers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
    System.out.println(list);
}

// 使用下界通配符
List<Number> numberList = new ArrayList<>();
addIntegers(numberList); // 输出:[1, 2, 3]

区别

  • <? extends T>:只能读取,不能写入(除 null)。
  • <? super T>:只能写入(可以写入 T 类型或其子类型)。

4. 泛型的类型擦除

>[!ERROR] >java的泛型是伪泛型 >只在编译时有效

Java 的泛型在编译时会被 类型擦除,即泛型信息在运行时被移除。这意味着:

  1. 类型参数被替换为它的边界(默认是 Object
  2. 无法在运行时获取泛型类型

示例

java
List<String> stringList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();

// 运行时两者实际上是相同的类型
System.out.println(stringList.getClass() == intList.getClass()); // 输出:true

限制

  • 不能创建泛型数组
java
List<String>[] listArray = new List<String>[10]; // 编译错误
  • 无法使用基本类型作为泛型类型
java
List<int> intList = new ArrayList<>(); // 编译错误

5. 泛型的限制

  1. 不能实例化泛型类型参数
java
public class Box<T> {
    // 编译错误,不能直接实例化 T
    // T item = new T();
}

原因:类型擦除使得在运行时无法确定具体类型。

  1. 静态上下文中不能使用泛型
java
public class Box<T> {
    // 静态变量不能使用 T
    // private static T item;
}
  1. 泛型类型不能是基本类型: 必须使用包装类代替,如 Integer 代替 int

6. 常用泛型类

ArrayList

ArrayList<E> 是最常用的泛型集合,用于存储动态数组。

HashMap

HashMap<K, V> 用于存储键值对。

Comparator

Comparator<T> 用于实现泛型比较器。


7. 总结

  • 泛型的核心概念是通过类型参数化,提高类型安全性、代码复用性和可读性。
  • 常见语法包括泛型类、泛型方法和泛型接口。
  • 通配符<?>, <? extends T>, <? super T>)是泛型中灵活处理类型的一种方式。
  • 了解泛型的限制和类型擦除有助于编写更加健壮的代码。

泛型是 Java 中处理数据类型的基础工具,熟练掌握它可以显著提高代码的灵活性和质量。

评论区

欢迎留言、补充或勘误。

xiaoba.blog