? extends T只能读不能写,因为编译器无法确定具体子类型,为保障类型安全禁止add等写操作;读取时返回T类型,可安全调用其方法或通过instanceof判断后转型。

为什么 ? extends T 只能读不能写
因为编译器无法在运行前确认你往集合里塞的具体是哪个子类型。比如 List extends Number> 可能是 ArrayList<integer></integer>,也可能是 LinkedList<double></double>,甚至 Arrays.asList(new BigDecimal(1)) —— 这时如果允许 add(new Integer(42)),就可能破坏原始集合的类型一致性。
所以 Java 直接禁止对 ? extends T 类型的集合调用任何带参数的写操作方法(add、set、addAll 等),只保留安全的读操作(get、iterator()、toArray())。
常见错误现象:error: no suitable method found for add(Integer),哪怕你确定底层是 ArrayList<integer></integer> 也没用 —— 类型系统不看实际运行时类型,只看引用声明。
如何安全地从 List extends Number> 中读取并处理子类型值
读取时返回的是 Number,不是具体子类,但你可以用 instanceof 或 getClass() 做运行时判断,再向下转型(需谨慎);更推荐用多态方式处理,比如调用 doubleValue()、longValue() 等统一接口方法。
-
get(0)返回Number,可直接调用doubleValue()、toString()等父类方法 - 若需子类特有行为(如
BigInteger.bitCount()),必须先if (n instanceof BigInteger)再强转 - 避免无条件强转:
(Integer) list.get(0)在List extends Number>指向Double实例时会抛ClassCastException
示例:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
void processNumbers(List extends Number> nums) {
for (Number n : nums) {
System.out.println("as double: " + n.doubleValue());
if (n instanceof BigDecimal bd) {
System.out.println("scale: " + bd.scale());
}
}
}
? extends T 和 T、? super T 的使用边界在哪
三者本质是「PECS」原则(Producer Extends, Consumer Super)的体现,关键看集合角色:
- 当集合只作为数据提供方(你只调用
get、stream()、forEach()),用? extends T - 当集合只作为数据接收方(你只调用
add、addAll),用? super T - 当集合既读又写且类型完全固定,就用具体类型
T(如List<string></string>)
混淆后果:用 List super Integer> 去遍历,get(0) 返回的是 Object,没法直接当 Integer 用;反过来用 List extends Integer> 就等价于 List<integer></integer>(因为 Integer 是 final 类),没实际扩展意义。
泛型通配符在方法参数中的典型实操模式
对外暴露 API 时,优先用通配符放宽入参限制,提升调用方灵活性;内部实现仍按具体类型处理。
- 接受任意
Number子类列表:用void sum(List extends Number> list),而不是void sum(List<number> list)</number>(后者无法传入ArrayList<long></long>) - 返回值一般不用通配符(如
List extends Number>),因为调用方还得自己处理通配符带来的读写限制;更常用List<number></number>或具体类型,或封装为不可变视图(Collections.unmodifiableList(...)) - 注意桥接方法和类型擦除影响:泛型方法签名在字节码中会被擦除,反射获取泛型信息需通过
Method.getGenericParameterTypes(),不能只看getParameterTypes()
真正容易被忽略的点是:通配符解决的是引用层面的兼容性,它不改变集合的实际类型,也不提供运行时类型检查能力。所有类型安全都发生在编译期,一旦绕过(比如用反射或原始类型),风险得自己兜底。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










