? super t 的核心是实现写入场景的逆变安全性,即允许向泛型集合添加t及其子类对象,确保编译期类型安全,但读取时只能返回object。

下界通配符 ? super T 的核心,是让一个泛型容器“能收下 T 及其子类”,而不是“只认 T”。它不改变 Java 泛型本身的不变性,而是通过编译期约束,在写入时提供安全、宽松的兼容性——这就是所谓 Consumer-Super(CS)逆变机制的本质。
它解决的实际问题:往不同层级的集合里统一写入 T
比如你有一堆 Integer,想加进 List<integer></integer>、List<number></number> 或 List<object></object>。这三个类型互不继承,不能直接用同一个方法参数接收。而 List super Integer> 就是它们的公共契约:只要能装得下 Integer,就符合要求。
-
List<integer></integer>:能装Integer✅ -
List<number></number>:Integer是Number子类,赋值安全 ✅ -
List<object></object>:所有对象都能存进Object容器 ✅
为什么叫“逆变”?不是“协变”
协变(? extends T)是“子类型变宽”:如果 Integer ≤ Number,那么 List<integer></integer> ≤ List<number></number> —— 但 Java 泛型默认不支持这种赋值,? extends 是模拟协变,用于读取。
逆变(? super T)方向相反:当 Integer ≤ Number,我们反而允许 List<number></number> 当作 List<integer></integer> 的“更通用容器”来用——即“类型越宽,越能接收具体值”。这正是消费者场景需要的逻辑:目标越泛化,接纳能力越强。
逆变的安全性来自两层限制
编译器只放行两类操作,缺一不可:
-
只允许写入 T 或其子类:比如
List super Integer>可以add(new Integer(42)),也可以add((short)1)(因为Short是Number,但不是Integer子类 → ❌ 不允许),确保不会塞进不兼容类型 -
读取只能返回 Object:因为实际可能是
List<object></object>,里面早被别人塞过String或Thread,编译器无法保证取出的是Integer,所以强制按最顶层类型处理
典型逆变应用:工具方法里的“数据搬运工”
像 Collections.copy(src, dest) 或自定义的收集方法,都依赖这个机制:
public static <t> void addAll(List super T> dest, T... elements) {
for (T e : elements) {
dest.add(e); // 安全:e 肯定能放进 dest 所指的任意超类容器
}
}</t>
调用时可以传 addAll(numbers, 1, 2, 3)(numbers 是 List<number></number>),也能传 addAll(objs, "a", "b")(objs 是 List<object></object>)。同一段代码,适配多个目标类型,且无运行时转型风险。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











