PECS原则是为边界清晰的流转而设:只读用? extends T确保安全产出,只写用? super T确保宽泛容纳,成对使用构成类型安全的数据管道,避免强转与运行时异常。

PECS原则不是为“转换”而生,而是为“边界清晰的流转”而设——它不改变数据本身,但让不同泛型类型的集合之间能安全、直接地传递元素,无需强转、擦除或中间包装。
只读端用 ? extends T:确保上游能稳稳产出目标类型
当方法只从集合中读取数据,并按T或其父类使用时,声明参数为 List extends T>,就告诉编译器:“我只要能当T用的东西,不管它具体是哪个子类”。
- 例如:
void processNames(List extends Person> src)可接收List、List,调用src.get(0).getName()安全合法 - 禁止
src.add(...)(除null),因为编译器无法确认底层实际类型,加错会破坏一致性 - 这一步不转换数据,但解除了“必须传
List”的僵化限制
只写端用 ? super T:确保下游能宽泛容纳目标类型
当方法主要向集合中添加T或其子类实例,且读取极少或仅作Object处理时,用 Collection super T>,意思是:“我能装下T,不管你是List
- 例如:
void collectIds(Collection super Integer> dest)可接收ArrayList或LinkedList -
dest.add(new Integer(100))总是安全的,因为Integer一定能放进Number或Object容器 - 但
dest.iterator().next()只能赋给Object,不能直接当Integer用——这不是缺陷,而是边界的诚实表达
成对出现:copy、filter、map 等通用操作的核心骨架
真实数据流很少单向读或单向写,而是“从A读 → 加工 → 往B写”。PECS在方法签名中左右呼应,构成类型安全的数据管道。
- 标准库中的
Collections.copy(dest, src)就是典型:dest是List super T>,src是List extends T> - 自定义拷贝方法:
public staticvoid copy(List extends T> src, List super T> dest) - 调用自由:
copy(students, persons)(Student→Person)、copy(people, objects)(Person→Object)全部通过编译,且全程无强制转换
避开“假装转型”的陷阱:PECS 不是语法糖,而是意图声明
很多人试图用 List 或原始类型绕过泛型限制,结果换来运行时异常或逻辑漏洞。PECS把“我只读”和“我只写”变成编译期契约:
- 不用
(Person) list.get(0)——? extends Person已保证返回值可当Person用 - 不用提前把
Student转成Object再塞进集合 ——? super Student允许原样写入 - 错误写法如
List extends Number> nums = new ArrayList直接编译失败,问题暴露在开发阶段(); nums.add(new Double(1.0));










