应按PECS原则选择通配符:输出用? extends T保障读取安全,输入用? super T放宽写入范围;如logAll(List<? super String>, List<String>)支持宽泛目标集合,getNumbers()返回List<? extends Number>确保元素可安全调用doubleValue()。

关键在于按用途选对通配符边界:输入用 ? super T 放宽接纳范围,输出用 ? extends T 保障读取安全。不是接口定义里硬写通配符,而是在方法参数或返回类型中动态应用。
输入参数用 ? super T:让接口能接收更宽泛的容器
当方法要往集合里写数据,就用下界通配符。它允许传入 T 或其任意父类的集合,只要能存下 T 就行。
- 比如日志批量写入:
void logAll(List<? super String> targets, List<String> logs),传ArrayList<Object>、ArrayList<CharSequence>都合法 - 可以安全调用
targets.add("msg"),因为所有目标集合都承诺能装下 String - 但读取时只能得到
Object,不能直接转成String—— 这是设计换来的写入自由
输出结果用 ? extends T:让调用方拿到可安全使用的值
当方法返回一个只读集合,就用上界通配符。它保证所有元素都是 T 或其子类,调用方能按 T 安全使用。
- 比如统一读取数字:
List<? extends Number> getNumbers(),返回ArrayList<Integer>或ArrayList<Double>都行 - 调用方能遍历并调用
doubleValue(),因为每个元素都继承自Number - 但不能往这个集合里 add 任何东西(除了 null),编译器禁止——这是为读取安全做的限制
组合场景用嵌套通配符:比如 Predicate<? super T>
当接口本身是泛型函数式接口,把它作为参数传入时,常配合通配符提升兼容性。
立即学习“Java免费学习笔记(深入)”;
- 例如过滤方法:
<T> List<T> filter(List<T> data, Predicate<? super T> p) - 传入
Predicate<Object>或Predicate<CharSequence>都可以处理String列表 - 因为谓词只要能处理 T 或它的父类,逻辑上就足够判断 T 的行为,且不会越界
牢记 PECS 原则,别反着用
Producer Extends,Consumer Super —— 看集合是“产出”还是“消费”数据来决定边界方向。
- 集合用来往外给数据(如返回值、foreach 源)→ 用
? extends T - 集合用来往里塞数据(如方法参数、add 目标)→ 用
? super T - 混用场景(如 copy(src, dst)):src 用
? extends T,dst 用? super T,刚好匹配


















