? extends T 的核心作用是安全读取T及其子类实例并禁止写入(null除外),适用于需统一处理多种子类型集合且仅读取的场景,如遍历或统计Number子类列表。

上限通配符 ? extends T 的核心作用是放宽读取兼容性,同时严格禁止不安全的写入操作——它不是在说“这个集合装的是 T 或子类”,而是在说“你可以安全地从中取出 T 类型的值,但不能往里放任何具体实例(null 除外)”。
什么时候该用 ? extends T
当你需要一个方法能接收多种子类型集合,且只读取数据时,就适合用上限通配符。比如处理不同数值类型:Integer、Double、BigDecimal 都是 Number 的子类,但 List<integer></integer> 和 List<double></double> 之间没有继承关系。不用通配符,就得为每种类型写重载方法。
- 典型场景:工具类中遍历、统计、格式化数值列表
- 常见位置:方法参数、局部变量、返回值声明(不能用于泛型类或接口的类型参数定义)
- 不能用于构造泛型实例,例如
new ArrayList extends Number>()是非法语法
为什么只能读、不能写
编译器只知道容器元素是 “某个未知的 T 子类型”,但无法确认具体是哪一个。比如 List extends Number> 可能是 ArrayList<integer></integer>,也可能是 LinkedList<double></double>。如果允许 add(new Integer(1)),而实际容器是 Double 列表,就会破坏类型一致性。
-
get(0)返回类型是Number(向上转型保证安全) -
add(...)、set(...)全部编译失败(除null) - 哪怕传入
new Number()(抽象类不可实例化)也不行,因为不存在满足条件的具体实例
如何配合方法设计发挥最大价值
把 ? extends T 当作“生产者”信号来用,符合 PECS 原则(Producer Extends, Consumer Super)。例如一个通用打印方法:
立即学习“Java免费学习笔记(深入)”;
public static void printValues(Collection<? extends Number> data) {
for (Number n : data) {
System.out.println(n.doubleValue()); // 安全调用 Number 方法
}
}
- 可传入
Collection<Integer>、Set<BigDecimal>、ArrayList<Float>等任意 Number 子类型集合 - 内部可放心调用
doubleValue()、intValue()等 Number 定义的方法 - 若去掉
extends Number,n.doubleValue()就会编译报错(Object 没有该方法)
注意多边界与接口实现的写法
extends 在这里不单指类继承,也涵盖接口实现。当需要多个能力约束时,可用 & 连接:
-
<T extends Comparable<T> & Serializable>:T 必须可比较且可序列化 -
Collection<? extends Comparable<String> & Cloneable>:元素需同时实现这两个接口 - 类必须写在最前,如
<T extends Number & Runnable>合法,<T extends Runnable & Number>编译报错


















