编译器拒绝向 List<? super Integer> 添加 Double,因为 Double 不是 Integer 的子类,无法保证所有可能的实际类型(如 List<Integer>)都能安全接收;该检查在编译期完成,符合 PECS 原则中“Consumer Super”的设计意图。

因为编译器无法保证 List<? super Integer> 实际承载的类型能安全接受 Double。
编译器只知道下界,不知道具体类型
声明 List<? super Integer> 时,编译器只确认该列表的实际类型是 Integer 或它的某个父类——可能是 List<Integer>、List<Number>,也可能是 List<Object>。但编译器无法在编译期确定到底是哪一个。
-
List<Integer>:只接受Integer及其子类(如int自动装箱后的Integer) -
List<Number>:可接受Integer、Double、Float等所有Number子类 -
List<Object>:理论上能存任意对象,但泛型约束仍以Integer为下界,不意味着开放所有写入
由于存在 List<Integer> 这种合法且常见的可能性,而 Double 无法赋值给 Integer,编译器必须拒绝该操作,否则就可能在运行时破坏类型一致性。
写入安全的唯一依据是“T 及其子类”
? super Integer 的设计原则是:只允许写入 Integer 类型或它的子类(比如匿名子类、枚举常量等极少数情况),因为所有这些类型都能被安全地向上转型为 Integer 的任意父类。
立即学习“Java免费学习笔记(深入)”;
-
list.add(new Integer(42))✅ 安全:任何Integer实例都适配Integer、Number、Object -
list.add(100)✅ 安全:自动装箱为Integer -
list.add(new Double(3.14))❌ 不安全:Double不是Integer的子类,也不能赋值给Integer引用
这不是运行时检查,而是编译期硬性拦截
Java 编译器(javac)在泛型擦除前就完成全部类型校验。它不会等到运行时才判断能否加 Double,而是在解析代码时直接报错:Cannot resolve method 'add(java.lang.Double)' 或类似提示。
- 这个错误不是警告,不生成 class 文件
- 和
List<Integer>.add("abc")被拒逻辑一致:都是编译期静态类型不匹配 - 反射、强制转型、
instanceof都绕不过这一关
对比上界通配符更易理解差异
? extends Number 允许读取为 Number,但禁止写入(除 null);? super Integer 则相反:允许写入 Integer,但读取只能得 Object。这种不对称性正是 PECS 原则(Producer Extends, Consumer Super)的体现——它针对的是使用意图,而非语法对称。
- 你向
? super Integer写,说明你是“消费者”,只关心能不能塞进去Integer - 你不能塞
Double,不是因为编译器“保守”,而是因为语义上它不属于这个消费契约


















