? super T 表示下界通配符,允许向容器写入T及其子类对象,保障编译期类型安全;? extends T 表示上界通配符,仅支持读取T或其子类对象,禁止写入(除null外),二者遵循PECS原则:Producer-Extends、Consumer-Super。

因为消费者角色的核心动作是“往里放数据”,而 ? super T 能保证所有可写入的元素类型都兼容 T 及其子类,从而在编译期守住类型安全。
消费者要写入,不是读取
消费者(Consumer)指那些接受数据、把元素“塞进去”的场景,比如 Collection.add()、OutputStream.write() 或自定义的 copy-to-target 方法。这类操作的关键约束是:你得能放心地 put 一个 T 类型的对象,不能出现 ClassCastException 或编译报错。
-
List<? super Integer>表示这个列表可以是List<Integer>、List<Number>或List<Object>—— 它们的共同点是:都能合法接收Integer实例。 - 反过来看,如果用
List<? extends Integer>,它可能是List<Integer>,也可能是List<AtomicInteger>(假设存在),但你无法确定能否 add 任意Integer,因为底层实际类型可能更窄(比如只允许AtomicInteger),所以编译器禁止 add。
下界通配符保障写入自由
? super T 是下界通配符,它声明“这个容器至少能装 T 类型或更宽泛的类型”。这带来两个实际好处:
- 你可以安全调用
add(T):无论实际是List<Number>还是List<Object>,T(如 Integer)都是它们的合法元素。 - 读取时类型变宽:get 出来的元素只能当作
Object处理(因为父类不确定),但这对消费者角色不重要——它本就不依赖读取结果做类型敏感操作。
对比生产者就更清楚
生产者(Producer)专注“往外拿”,所以用 ? extends T:能确保 get 出来的东西一定是 T 或其子类,但牺牲了写入能力;消费者相反,放弃精确读取能力,换来了稳定写入能力。二者不可兼得,PECS 正是这种权衡的总结。
立即学习“Java免费学习笔记(深入)”;
本质上,super 对应“输入友好”——只要你是 T 或它的子类,就能进;extends 对应“输出友好”——只要你出来,就一定是 T 或它的子类。


















