? extends T 用于安全读取多种子类型数据并禁止不安全写入,只允许以T类型读取(如Number),add()等写入操作被禁止(null除外)。

用 ? extends T 是为了安全读取多种子类型的数据,同时禁止不安全的写入操作。它不是在描述“容器里装的是什么”,而是在声明“你能从里面安全拿到什么”——只能以 T 类型读取,不能往里加(null 除外)。
什么时候该用 ? extends T
当你需要一个方法能接收 不同子类型集合但只读取数据 时,就适合用上限通配符。比如处理数值类型:
-
List<Integer>、List<Double>、List<BigDecimal>都是Number的子类,但它们彼此没有继承关系 - 不用
? extends Number,就得为每种类型单独写重载方法 - 加上后,一个方法就能统一处理所有
Number子类的集合
它能做什么、不能做什么
编译器只知道元素是 “某个未知的 T 子类”,所以:
- ✅
get(i)返回类型是T(如Number),可直接调用T定义的方法,比如doubleValue() - ❌
add()、set()全部编译报错,哪怕传入new Integer(1)或new Number()(抽象类无法实例化)也不行 - ⚠️
add(null)是唯一允许的写入操作,但通常没实际意义
典型写法和常见误区
正确用法示例:
立即学习“Java免费学习笔记(深入)”;
public static void printNumbers(Collection<? extends Number> data) {
for (Number n : data) {
System.out.println(n.doubleValue()); // 安全调用
}
}
调用时可传入 ArrayList<Integer>、Set<Float>、LinkedList<BigDecimal> 等任意 Number 子类型集合。
容易踩坑的地方:
- 不能把
get(0)强转成具体子类,比如(Integer) list.get(0),除非先做instanceof检查 - 不能用作方法返回值类型,如
List<? extends Number> getNumbers(),否则调用方无法安全添加元素 - 不能出现在类或接口的类型参数定义中,例如
class Container<? extends Number>是非法语法
配合泛型方法提升灵活性
单独用 ? extends T 限制写入,但结合泛型方法参数,就能在读取后做进一步处理:
<T extends Comparable<T>> T max(List<? extends T> list)
这样既支持 List<String>,也支持 List<LocalDate>,内部仍用 ? extends T 接收保证输入兼容性,返回值却是确定的 T 类型。


















