Collections.addAll()使用? super T是因为其作为“消费者”需写入T类型元素,符合PECS原则中“Consumer Super”;? extends T适用于读取,禁止写入,故不适用。

Java 中 Collections.addAll 方法使用 ? super T 通配符,正是 PECS 原则中 “Consumer Super” 的典型体现——它把目标集合视为“消费者”,只负责接收(写入)类型为 T 的元素,从而在编译期就杜绝非法写入,保障类型安全。
为什么用 ? super T 而不是 ? extends T
因为 addAll 的作用是往集合里“加东西”,不是从集合里“取东西”。
- ? extends T 是为“读取”设计的:你可以安全地把每个元素当作 T 来用(如 get(0) 返回 T),但不能往里加任何具体类型(连 T 都不行),否则会破坏类型约束。
- ? super T 是为“写入”设计的:你只能往里加 T 或其子类实例(如 add(new T())),但取出来的元素只能当作 Object(或其最宽泛的上界),因为具体是什么父类不确定。
addAll 方法签名与类型安全机制
方法定义如下:
public static <T> boolean addAll(Collection<? super T> c, T... elements)
关键点:
-
c是消费者:它必须能容纳所有T类型的元素,所以它的实际类型可以是Collection<T>、Collection<Object>、Collection<Serializable>等——只要T是其元素类型的子类型即可; - 编译器检查:当你传入
Collection<Animal>并调用addAll(animalList, new Dog()),Dog是Animal的子类,符合? super Dog的约束(注意:这里T = Dog,所以? super Dog允许Animal或Object等); - 反例失败:若误传
Collection<Cat>给addAll(..., new Dog()),编译直接报错——因为Cat不是Dog的父类,不满足? super Dog。
一个直观例子
假设:
立即学习“Java免费学习笔记(深入)”;
class Animal { }<br>class Dog extends Animal { }<br>class Cat extends Animal { }以下调用全部合法:
-
addAll(new ArrayList<Animal>(), new Dog(), new Dog())✅ -
addAll(new ArrayList<Object>(), new Dog(), new Cat())✅(此时T会推断为Animal,因两者有公共父类) -
addAll(new ArrayList<Dog>(), new Dog())✅(Dog是自身父类)
但以下非法:
-
addAll(new ArrayList<Cat>(), new Dog())❌ 编译失败:Cat不是Dog的父类; -
addAll(new ArrayList<String>(), new Dog())❌ 同理,无继承关系。
它不保证运行时“语义安全”,只保编译期类型兼容
? super T 不关心业务逻辑是否合理。例如:
- 允许向
ArrayList<Number>添加Integer和Double——类型安全; - 也允许向
ArrayList<Serializable>添加任意可序列化对象(包括String、File、自定义类)——仍类型安全; - 但它不会阻止你把
File加进本该只存Number的业务容器中——那是设计层面的责任,PECS 只管“能不能加”,不管“该不该加”。



















