什么是泛型中的限定通配符和非限定通配符 ?

参考回答

在 Java 的泛型中,通配符用于定义一个范围不明确的类型参数。主要分为以下两类:

  1. 非限定通配符 (?):表示可以接受任何类型。
  • 例如:List<?> 可以表示 List<String>List<Integer> 等任意类型的列表。
  1. 限定通配符
  • 上界通配符

    (? extends T):表示可以接受类型 T或 T的子类。

    • 例如:List<? extends Number> 可以接受 List<Integer>List<Double>
  • 下界通配符

    (? super T):表示可以接受类型 T或 T的父类。

    • 例如:List<? super Integer> 可以接受 List<Integer>List<Number>

通配符的选择取决于代码的需求,尤其是对泛型数据的读写操作。


详细讲解与拓展

1. 非限定通配符 ?

? 表示未知的类型,适用于不关心具体类型,只需要在逻辑上统一处理的场景。它通常用于只读操作

示例:

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

List<String> stringList = List.of("A", "B", "C");
List<Integer> intList = List.of(1, 2, 3);

printList(stringList); // 输出 A, B, C
printList(intList);    // 输出 1, 2, 3

注意:在使用 ? 时,你无法往集合中添加元素(除了 null),因为它不知道 ? 具体代表什么类型。


2. 限定通配符

(1) 上界通配符 ? extends T
  • ? extends T 表示类型的上界是 T,可以是 TT 的子类。
  • 常用于只读操作,因为可以安全地读取为 T 类型,但无法向其中添加元素。

示例:

public double sumOfNumbers(List<? extends Number> list) {
    double sum = 0;
    for (Number num : list) {
        sum += num.doubleValue();
    }
    return sum;
}

List<Integer> intList = List.of(1, 2, 3);
List<Double> doubleList = List.of(1.1, 2.2, 3.3);

System.out.println(sumOfNumbers(intList));    // 输出 6.0
System.out.println(sumOfNumbers(doubleList)); // 输出 6.6

扩展知识点
上界通配符的设计是为了让泛型支持协变(Covariance)。比如 List<Integer>List<Double> 都可以被认为是 List<? extends Number>,即可以安全地读取Number

(2) 下界通配符 ? super T
  • ? super T 表示类型的下界是 T,可以是 TT 的父类。
  • 常用于写入操作,因为可以安全地向集合中添加 T 或其子类。

示例:

public void addNumbers(List<? super Integer> list) {
    list.add(1);
    list.add(2);
    list.add(3);
}

List<Number> numList = new ArrayList<>();
addNumbers(numList);

System.out.println(numList); // 输出 [1, 2, 3]

扩展知识点
下界通配符的设计是为了让泛型支持逆变(Contravariance)。比如 List<Number>List<Object> 都可以被认为是 List<? super Integer>,即可以安全地写入 Integer


3. 什么时候使用限定通配符?

  • 上界通配符 ? extends T 当你只需要读取数据时使用。例如计算集合中的总和。
  • 下界通配符 ? super T 当你只需要写入数据时使用。例如向集合中添加元素。
  • 非限定通配符 ? 当你只需要操作类型未知的集合,且不涉及具体的读写类型时使用。

4. 通配符和泛型方法的对比

使用通配符的泛型方法

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

使用泛型参数的泛型方法

public <T> void printList(List<T> list) {
    for (T item : list) {
        System.out.println(item);
    }
}

区别

  • 通配符关注的是“类型范围”(上界或下界)。
  • 泛型方法关注的是“参数类型的一致性”。

总结

  1. 非限定通配符 ? 适合类型未知的情况。
  2. 上界通配符 ? extends T 适合只读场景,支持协变。
  3. 下界通配符 ? super T 适合只写场景,支持逆变。
  4. 通配符让泛型在灵活性和类型安全性之间达成平衡。

发表回复

后才能评论