PECS原则是:Producer用extends(只读时用上界),Consumer用super(只写时用下界);Extends对应数据“出口”,Super对应数据“入口”,核心在于方法中add和get的操作方向。

记住 PECS 的关键是抓住“动作”和“方向”:谁在往外给数据,谁在往里收数据。不是记单词缩写,而是看方法里对集合做了什么操作。
Producer 用 extends:只读不写时选上界
当一个方法只从泛型集合中取元素(比如遍历、查找、计算总和),它就是生产者——把数据“生产”出来供你用。这时用 ? extends T,保证你能安全地把每个元素当作 T 或其父类来用。
- 能做的事:调用 get()、遍历、返回 T 类型值
- 不能做的事:调用 add()(除 null 外),因为编译器不知道底层到底是 AppleList 还是 BananaList
- 例子:
void printAll(List<? extends Fruit> fruits)可以接收List<Apple>或List<Banana>,但不能往里面加新水果
Consumer 用 super:只写不读时选下界
当一个方法只往集合里塞数据(比如批量添加、归并、填充默认值),它就是消费者——把外部数据“消费”进去。这时用 ? super T,保证你能放心地把 T 类型的对象放进去。
- 能做的事:调用 add(T),传入 T 或其子类实例(如 add(new Apple()) 到
List<? super Fruit>) - 不能做的事:安全地 get() 并当作具体类型使用(比如取出来直接当 Fruit 用会报错,只能当 Object)
- 例子:
void addApples(List<? super Apple> basket)可以接收List<Fruit>、List<Object>,甚至List<Apple>
混用场景:先分清主次操作
如果一个方法既读又写,通配符往往不合适,优先考虑用具体类型参数(如 <T> void process(List<T> list))。PECS 是为“单向操作”设计的安全边界。强行套用会导致要么读不了,要么写不了。
立即学习“Java免费学习笔记(深入)”;
- 常见误区:想用
List<? extends Number>来 add(Integer) —— 不行,这是生产者语义 - 正确做法:需要写入就换用
List<? super Integer>,哪怕你只是临时存一下 - 工具类参考:Collections.copy(dest, src) 中 dest 是 consumer,用
? super T;src 是 producer,用? extends T
一句话口诀辅助记忆
Extends 是“出口”,Super 是“入口”。数据从集合里出来 → extends;数据往集合里进 → super。不用背英文缩写,盯住方法体里那几行 add 和 get 就够了。


















