通配符解决泛型不变性导致的类型兼容问题:无界?用于只读操作;? extends T用于生产者(读取T或其子类);? super T用于消费者(写入T或其父类);遵循PECS原则。
通配符在方法参数中主要解决泛型“不变性”带来的类型兼容问题。Java 中
不是
的子类型,哪怕
是
的子类——这导致很多直觉上合理的调用会编译失败。通配符通过放宽类型约束,让方法能安全地接受更宽泛的泛型实参,从而提升 API 的通用性和复用性。
无界通配符 ?:只读场景下的通用入口
当你只需要遍历、检查或消费集合内容,不关心具体元素类型时,
是最轻量的选择。
方法签名示例:
可传入
、
、
等任意类型列表
内部只能把元素当作
处理,不能调用
(除
外),因为编译器无法确认实际类型
适合日志打印、统计大小、判空等与类型无关的操作
上界通配符 ? extends T:作为“生产者”时放宽输入
当方法从泛型结构中**读取**数据,并期望这些数据是某个类型的实例(或其子类)时,用
。
典型场景:计算总和、查找最大值、批量转换
示例:
可接收
、
、
可以安全地将每个元素当作
使用(如调用
)
但不能向该列表中添加任何元素(包括
或其子类),因为实际类型可能是
,加
会破坏类型安全
下界通配符 ? super T:作为“消费者”时拓宽写入目标
当方法需要向泛型结构中**写入**数据,且只要求目标容器能容纳某类型或其父类时,用
。
典型场景:批量添加、归并、收集结果
示例:
可接收
、
、
可以安全地
,因为所有这些目标类型都声明能存下
但从该列表中读出的元素只能当作
使用(因上界未知),无法直接调用
遵循 PECS 原则,避免误用
Producer-Extends, Consumer-Super —— 这是判断用哪个通配符的核心口诀。
参数用于“往外拿数据”(生产者)→ 用
参数用于“往里塞数据”(消费者)→ 用
如果既要读又要写,就别用通配符,保持精确类型(如
)
返回值类型一般不用通配符(如避免
),否则调用方难以处理,应返回明确泛型如
ListListStringObject?public void printAll(List> list)ListListListObjectadd()null? extends Tdouble sum(List extends Number> numbers)ListListListNumberdoubleValue()NumberListDouble? super Tvoid addIntegers(List super Integer> dst)ListListListadd(new Integer(42))IntegerObjectintValue()? extends T? super TListList extends String>List