pecs原则指导泛型集合安全读写:只读用? extends t(生产者),只写用? super t(消费者);前者支持向上转型读取,后者支持向下转型写入。

泛型通配符的 PECS 原则(Producer Extends, Consumer Super)本质是指导我们如何安全地读写泛型集合——当集合只用来“往外拿数据”,就用 ? extends T;当集合只用来“往里塞数据”,就用 ? super T。
用 ? extends T 读取子类对象(生产者场景)
当你有一个方法,只从集合中获取元素并当作 T 或其父类型使用,但不向其中添加新元素(或只添加 null),就应该用 Collection extends T>。这保证了取出的对象一定是 T 的实例或其子类,可以安全地向上转型。
- 例如:
void printNames(List extends Person> people)可以接收List<student></student>或List<teacher></teacher>,因为它们都“产出”Person及其子类 - 但不能往该集合中 add 任意具体类型(如
add(new Student())是编译错误),因为编译器无法确认实际类型是否匹配 - 唯一允许的写入是
null,因为它在任何引用类型中都合法
用 ? super T 写入父类兼容对象(消费者场景)
当你有一个方法,主要向集合中添加 T 类型或其子类的实例,但很少读取、或读取后只按 Object 处理,就应使用 Collection super T>。它放宽了写入限制,确保所有 T 的子类都能被接受。
- 例如:
void addStudents(Collection super Student> target)可以接收Collection<student></student>、Collection<person></person>,甚至Collection<object></object> - 你可以安全地
add(new Student())或add(new Graduate())(假设Graduate是Student子类),因为它们都是T的实例 - 但从中读出的元素只能当作
Object使用(除非显式强制转换),因为实际类型可能是更宽泛的父类
真实流转场景中的组合使用
在数据加工链路中,PECS 往往成对出现:上游用 extends 安全输出,下游用 super 安全接收。比如一个通用拷贝工具:
public static <t> void copy(List extends T> src, List super T> dest) {
for (T item : src) {
dest.add(item); // ✅ src 能提供 T,dest 能接收 T
}
}</t>
- 调用时可传入
copy(students, persons)(Student→Person)、copy(people, objects)(Person→Object) - 这种设计让方法具备最大兼容性,又不牺牲类型安全
- 若强行统一用
List<t></t>,调用方就得频繁做无意义的类型擦除或中间转换
避开常见误区
PECS 不是语法糖,而是类型系统对“使用意图”的显式声明。误用会导致编译失败或运行时隐患:
- 把
ArrayList<object></object>当作List extends String>传入?❌ 编译报错——Object不是String的子类 - 在
List super Integer>中get(0)后直接强转Integer?⚠️ 危险——实际可能是List<number></number>,存了Double - 为图省事全用
List<object></object>?❌ 放弃了泛型的编译期检查,等于退化到 Java 1.4 时代






