List<? extends Number> 是只读集合,可安全读取为Number类型但禁止写入(null除外),适用于只读场景;需写入时应使用List<Number>。

读取安全:所有子类都能向上转型为 Number
声明 List<? extends Number> 表示这个集合实际装的是某个**确定但未知的** Number 子类型,比如 Integer、Double 或 BigDecimal。无论具体是哪一种,它们都继承自 Number,所以调用 get() 返回的值一定能安全赋给 Number 类型变量:
-
Number n = list.get(0);—— 合法,因为Integer、Double等都是Number的子类 - 你可以直接调用
n.doubleValue()、n.toString()等Number定义的方法,无需强转
写入禁止:编译器无法验证添加值是否匹配底层真实类型
你写 list.add(new Integer(42)) 时,编译器必须保证该操作对所有可能的实际类型都成立。但它知道这个引用可能指向:
-
ArrayList<Double>→ 加Integer会破坏类型一致性 -
LinkedList<BigDecimal>→ 加Integer同样不合法 - 哪怕你心里清楚它其实是
ArrayList<Integer>,编译器也只看声明,不看运行时实例
只要存在一种可能失败的情形,编译就拒绝——这是 Java 泛型“类型安全优先”原则的体现。
null 是唯一允许的写入值
null 不携带任何类型信息,可赋值给任意引用类型,不会引发类型冲突:
立即学习“Java免费学习笔记(深入)”;
-
list.add(null);—— 编译通过 - 但这只是语法特例,不能替代业务上的写入需求
正确选型:按读写意图选择泛型形式
不要为了“看起来通用”而滥用 ? extends T。它本质是“只读视图”,适合做方法参数:
- ✅
void printNumbers(List<? extends Number> src)—— 只读场景,安全又灵活 - ✅
List<Number> numbers = new ArrayList<>();—— 需要存多种数字子类时,用具体上界类型 - ❌
List<? extends Number> list = new ArrayList<>();—— 自己新建却还想add,逻辑矛盾


















