? super T 可写入T及其子类,因实际类型必为T或其父类,类型安全;? extends T 禁止写入(除null),因实际类型是未知的T子类,写入可能破坏类型约束。

为什么 ? super T 能往集合里写元素,而 ? extends T 不行
因为类型擦除后,JVM 需要确保写入安全:编译器只允许向声明为 ? super T 的集合中添加 T 或其子类实例,这样无论实际类型是 T、Object 还是某个中间父类(比如 Number),都能保证类型兼容。反过来,? extends T 表示“某个未知的 T 子类”,你无法确定它具体是什么,所以禁止写入(除了 null)——否则可能破坏集合的真实类型约束。
常见错误现象:Collection extends Number> nums = new ArrayList 会编译失败,提示 “add(capture#1 of ? extends Number) in Collection cannot be applied to (Double)”。
Collection super Integer> 允许写入哪些值
只要值的静态类型是 Integer 或其子类(如 Long、Short 在显式转型后不被接受,但 Integer 实例本身可以),就能写入。注意:运行时类型无关紧要,关键看编译期类型检查。
-
Integer实例 —— 安全,直接允许 -
new Long(1)—— 不允许,因为Long不是Integer的子类(二者同属Number,但无继承关系) -
null—— 总是允许,因为它是所有引用类型的子类型 - 如果实际集合是
ArrayList,那么写入Integer没问题;但如果集合是ArrayList,也一样能接收Integer,因为Integer是Object的子类
典型使用场景:泛型工具方法中做“消费者”操作
最常见的是类似 Collections.copy() 或自定义的批量添加逻辑,目标集合需要接收源数据,但又不想限定死具体父类类型。
例如这个工具方法:
public static <t> void addAll(Collection super T> dest, T... items) {
for (T item : items) {
dest.add(item); // ✅ 编译通过
}
}</t>
调用时可以灵活传入不同父类型的集合:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
addAll(new ArrayList(), new Integer(1), new Integer(2)); addAll(new ArrayList- 但不能写成
addAll(new ArrayList—— 因为(), new Integer(1)) Integer不是String的子类,违反? super T约束
容易忽略的坑:读取返回值的类型是 Object,不是 T
虽然你能往 Collection super T> 里安全地写 T,但从中读取元素时,编译器只知道它是个 Object(因为上界是未知父类),不能自动转成 T。
比如:
Collection super Integer> c = new ArrayList<number>(); c.add(123); Integer i = c.iterator().next(); // ❌ 编译错误:Type mismatch: cannot convert from Object to Integer</number>
必须显式转型:
Integer i = (Integer) c.iterator().next(); // ✅ 但有 ClassCastException 风险
所以,? super T 天然适合「只写不读」或「读取后手动处理」的场景。如果既要写又要读且希望类型安全,应避免通配符,改用具体泛型参数(如 )或者用有界类型变量(如 不合法,需换思路)。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










