Java泛型通配符:上界与下界,简单理解

为什么需要泛型通配符?

在Java泛型中,我们常常会遇到需要处理一类集合的情况:比如,一个方法可能需要同时处理List<Integer>List<Double>List<Number>等不同类型的集合,但又不想为每个类型单独写一个方法。这时候,泛型通配符就能派上用场了。

通配符用?表示,分为两种:上界通配符? extends T)和下界通配符? super T),它们能帮助我们统一处理不同子类型的泛型集合,同时保证类型安全。

一、上界通配符(? extends T

什么是上界通配符?

上界通配符表示:集合中的元素类型是T子类或T本身
语法:List<? extends T>List<? extends Number>(假设TNumber)。

举个例子

假设我们有一个类层次结构:Object是根类,Number继承ObjectIntegerDouble继承Number
如果我们想写一个方法,能打印所有Number类型(及其子类)的集合元素,可以这样写:

// 打印List中所有元素
public static void printNumbers(List<? extends Number> list) {
    for (Number num : list) { // 元素一定是Number类型,安全
        System.out.println(num);
    }
}

// 调用方法
List<Integer> intList = Arrays.asList(1, 2, 3);
List<Double> doubleList = Arrays.asList(1.1, 2.2, 3.3);
List<Number> numberList = Arrays.asList(10, 20.5, 30);

printNumbers(intList);   // 正确(Integer是Number子类)
printNumbers(doubleList); // 正确(Double是Number子类)
printNumbers(numberList); // 正确(Number是上界)

上界通配符的特点

  1. 只能获取元素,不能添加元素
    因为编译器不知道集合具体是哪个子类(如List<Integer>List<Double>),如果尝试添加元素,类型可能不匹配。
    ❌ 错误示例:list.add(new Integer(10));
    ✅ 正确:只能获取元素,返回类型是T(如Number)。

  2. 返回元素类型安全
    元素一定是T或其子类,因此可以用T类型接收(如Number)。

二、下界通配符(? super T

什么是下界通配符?

下界通配符表示:集合中的元素类型是T父类或T本身
语法:List<? super T>List<? super Number>(假设TNumber)。

举个例子

假设我们想写一个方法,能往集合中添加Number类型及其子类(如IntegerDouble)的元素,同时处理不同父类的集合(如List<Object>List<Number>):

// 往集合中添加Number类型及其子类
public static void addNumbers(List<? super Number> list) {
    list.add(new Integer(10));  // 正确(Integer是Number子类)
    list.add(new Double(20.5)); // 正确(Double是Number子类)
    list.add(new Number(30));   // 正确(Number是上界)
}

// 调用方法
List<Object> objList = new ArrayList<>();
List<Number> numList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();

addNumbers(objList);   // 正确(Object是Number的父类)
addNumbers(numList);   // 正确(Number是Number的父类)
addNumbers(intList);   // 正确(Integer是Number的子类,可作为父类集合处理)

下界通配符的特点

  1. 只能添加元素,不能获取具体类型元素
    因为集合可能是ObjectNumber等父类集合,元素类型不确定,只能返回Object
    ❌ 错误示例:Number num = list.get(0);
    ✅ 正确:只能获取Object类型元素。

  2. 添加元素类型安全
    可以添加T或其子类,编译器能保证类型匹配。

三、关键区别与场景总结

通配符类型 定义 核心特点 适用场景
? extends T 元素是T的子类或T本身 只能获取元素(返回T),不能添加元素(除null) 需要读取不同子类集合的元素
? super T 元素是T的父类或T本身 只能添加元素(T或其子类),不能获取具体类型元素(只能返回Object) 需要往不同父类集合添加元素

四、避坑指南

  1. 上界通配符不能写“T”,下界通配符不能写“T”
    通配符是“不确定类型”的占位符,而T是类型参数(需明确指定类型)。例如:
   // 错误写法(上界通配符不能用T作为返回类型)
   public static <T extends Number> T getFirst(List<T> list) { ... }
   // 正确写法(用类型参数T)
  1. 避免过度使用通配符
    如果方法只需要List<Integer>,直接使用List<Integer>更明确。通配符用于“未知具体类型”的场景。

五、一句话总结

  • 上界通配符(? extends T:处理“所有T的子类”,只读不写
  • 下界通配符(? super T:处理“所有T的父类”,只写不读具体类型

掌握这两个规则,就能灵活应对Java泛型中不同子类/父类集合的统一处理问题了!

小夜