pecs原则是类型安全的api设计指南:? extends t用于只读生产者(可安全读取t及子类,禁止写入),? super t用于只写消费者(可安全写入t及子类,读取仅得object)。

PECS 原则不是语法规定,而是类型安全与 API 设计的实践指南:它帮你判断该用 ? extends T 还是 ? super T,核心就看这个泛型参数在方法里是“往外吐数据”还是“往里收数据”。
只读场景:用 ? extends T(生产者)
当你设计一个方法,只从集合中取值、不往里塞东西,它就是生产者。此时用 ? extends T,能接收 List<apple></apple>、List<banana></banana> 等所有 T 的子类型集合,且读出的每个元素都能安全当作 T 使用。
- 典型例子:打印水果列表、计算总重量、校验是否为空等只读操作
- 不能写入任何非 null 值——编译器禁止,因为不知道底层到底是
Apple还是Banana列表 - 允许读取,返回值类型自动向上转型为
T,比如fruitList.get(0)返回Fruit
只写场景:用 ? super T(消费者)
当你设计一个方法,只往集合里添加数据、不从中读取具体类型,它就是消费者。此时用 ? super T,能接收 List<fruit></fruit>、List<food></food> 等所有 T 及其父类的集合,确保 T 类型的对象一定能安全放进其中。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 典型例子:批量添加苹果到水果列表、收集日志对象到通用容器、归并子类型数据到父类集合
- 可以写入
T或其子类实例(如向List super Apple>中加RedApple) - 读取时只能得到
Object(因为下界太宽),所以一般不用于读取逻辑
常见工具方法里的 PECS 实际体现
JDK 自带的很多 API 都严格遵循 PECS,直接照着用就能体会它的合理性:
-
Collections.copy(List super T> dest, List extends T> src):目标列表是消费者(写入),源列表是生产者(读取) -
Stream<t>.collect(Collector super T, A, R>)</t>:收集器要接收T类型元素,所以是消费者 -
Comparator super T>:能比较T及其子类(比如用Comparator<number></number>比较Integer和Double)
别踩坑:什么时候不该用通配符?
如果一个泛型参数既要读又要写,就不要强行套 PECS——通配符会限制操作自由度。这时候应该用具体类型参数或泛型方法:
- 错误示范:
void swap(List extends Number> list, int i, int j)—— 无法同时读和写 - 正确做法:
<t> void swap(List<t> list, int i, int j)</t></t>,让类型统一可读可写 - 集合自身声明(如
class Stack<e></e>)也不用通配符,那是定义阶段,不是使用阶段
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










