pecs原则是类型安全驱动的实用经验:只读用? extends t,只写用? super t;collections.copy等jdk方法即典型应用,核心是保证运行时不抛classcastexception。

PECS 原则不是语法规定,而是类型安全驱动下的实用经验:只读用 ? extends T,只写用 ? super T。关键不在“能不能编译”,而在于“编译器能否保证运行时不抛 ClassCastException”。
当方法只从集合取数据(Producer 场景)
比如一个工具方法,要遍历并打印所有水果名称,但不往里加新元素:
- 参数声明为
List extends Fruit>,就能接收List<apple></apple>、List<banana></banana>,甚至List<fruit></fruit> - 循环中每个元素都能安全转成
Fruit,因为所有子类都继承自它 - 禁止调用
add()(除null),否则编译失败——因为编译器无法确认该集合实际装的是哪种子类
当方法只向集合存数据(Consumer 场景)
比如一个批量添加苹果的工具方法,希望它既能加到 List<fruit></fruit>,也能加到 List<object></object>:
- 参数声明为
List super Apple>,就兼容List<apple></apple>、List<fruit></fruit>、List<object></object> - 可以安全写入
Apple实例,因为所有这些集合都至少能容纳Apple及其父类 - 读取时只能当作
Object接收(例如get(0)返回Object),因为具体类型不确定
常见误用与规避方式
容易混淆的点是把通配符当成“泛化输入”,其实它本质是“放宽输入约束,同时限制操作能力”:
- 不要对
List extends T>调用add(),哪怕传null以外的任何值都会报错 - 不要对
List super T>期望读出T类型——返回值只能是Object,需手动判断或转型 - 如果方法既读又写,别硬套 PECS;直接使用具体类型参数(如
<t> void process(List<t> list)</t></t>)更清晰安全
结合标准库理解更直观
JDK 自己就在大量使用 PECS:
-
Collections.copy(List super T> dest, List extends T> src):目标列表必须能接收T(下界),源列表必须能提供T或其子类(上界) -
Arrays.asList(T... a)返回List<t></t>,但当你传String[],结果是List<string></string>,不是List<object></object>——不变性要求你显式用通配符适配
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











