? super t 的核心作用是放宽写入限制,允许向泛型容器中安全写入 t 及其子类实例,实现“写入友好”;它不支持多态读取,读取时只能视为 object,适用于消费者(consumer)场景。

逆变通配符 ? super T 的核心作用是**放宽写入限制,允许向泛型容器中安全地写入类型为 T 或其子类的实例**,从而支持泛型多态写入。它不提供多态读取能力,但让“写入”行为具备向上兼容性。
为什么需要 ? super T 来写入
Java 泛型是不变的(invariant),List<string></string> 并不是 List<object></object> 的子类型,哪怕 String 是 Object 的子类。直接传参会编译失败:
void addToList(List<object> list) { list.add(new Object()); }
List<string> strs = new ArrayList();
addToList(strs); // 编译错误!类型不匹配</string></object>
而用 ? super T 声明参数,就告诉编译器:“我只往里写 T 及其子类,你只要保证容器能装下 T 就行”。这样,List<object></object>、List<charsequence></charsequence>、List<string></string> 都满足 ? super String 的约束。
? super T 的典型写入场景
常见于工具方法,如集合填充、消费型操作(Consumer 模式):
- 向列表添加多种子类型实例(例如:把
String、StringBuilder都加进List super CharSequence>) - 使用
Collection super T>.add(T)接收任意上界容器 - 配合
java.util.Collections.copy()等 API 设计,实现“生产者-消费者”分离
实际写法与类型安全逻辑
声明方法时用 ? super T,调用时传入具体上界类型:
// 允许写入 String 及其子类(虽然 String 是 final,但逻辑通用)
public static void addNames(List super String> target) {
target.add("Alice"); // ✅ OK:String 是 ? super String 的合法元素
target.add("Bob"); // ✅ OK
// target.add(new Object()); // ❌ 编译错误:Object 不一定是 String 的父类(且超出了下界保障)
}
List<object> objs = new ArrayList();
List<charsequence> seqs = new ArrayList();
List<string> strs = new ArrayList();
addNames(objs); // ✅ Object 是 String 的父类
addNames(seqs); // ✅ CharSequence 是 String 的父接口
addNames(strs); // ✅ String 自身也满足 super String</string></charsequence></object>
关键点在于:编译器只允许写入 T 类型(或其子类)——因为所有上界类型(Object、CharSequence、String)都保证能接收 String 实例;但禁止读取为具体类型(如 String s = target.get(0) 会报错),因为返回类型只能是 Object(最宽上界)。
对比协变 ? extends T 理解差异
? extends T 支持多态读取(可安全读出 T 或其子类),但禁止写入(除了 null);? super T 正好相反:
-
List extends Number>→ 可读Number,不可写(除 null) -
List super Integer>→ 可写Integer(及int自动装箱),读出只能当Object
这就是 PECS 原则(Producer Extends, Consumer Super):你要“消费”数据(写入),就用 super。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











