? extends T 通配符保障读取安全:编译器确认元素均为T或其子类,可安全赋值给T并调用其方法;但禁止写入具体值(仅null例外),因底层类型未知,写入可能破坏类型一致性。

Java 中的 ? extends T 通配符本身不“限制读取”,而是保障读取的安全性——它让编译器能确认取出的每个元素至少是 T 类型,因此可安全调用 T 定义的方法;但同时,它彻底禁止写入具体值(null 除外),这是读取安全的前提。
为什么读取是安全的?
编译器知道集合中所有元素都属于 T 或其某个子类,虽然不确定具体是哪一个,但足以保证向上转型为 T 是类型安全的。
-
✅ 可以用 Number 接收:比如
List<? extends Number>中的get(0)返回Number,可直接调用doubleValue()、intValue() -
✅ 遍历也安全:for-each 循环中每个元素自动视为
Number,无需强转 -
❌ 不能用子类接收(不强制):你不能写
Integer i = list.get(0),因为实际可能是Double;必须先判断或接受上界类型
为什么写入被禁止?
写入操作会破坏类型一致性,而编译器无法验证风险。
-
❌ add(new Integer(1)) 编译失败:因为底层可能是
ArrayList<Double>,加 Integer 会混入非法类型 - ❌ set(0, new BigDecimal("2")) 同样失败:即使 BigDecimal 是 Number 子类,也无法确定容器是否允许它
- ✅ add(null) 允许:null 可赋值给任意引用类型,不破坏类型契约
常见误用与澄清
- ? extends Object ≠ ? extends Number:前者无约束,取出来只能当 Object 用;后者才真正启用 Number 的方法
- 不是“限制读取”,而是“限定读取结果的最小类型”:你读到的不是“不确定类型”,而是“确定可当 T 用的类型”
-
和泛型类定义中的
不同: 后者用于声明类型参数,前者用于变量/参数声明,用途和语义有本质区别


















