pecs原则指导泛型通配符选择:只读用? extends t(生产者),只写用? super t(消费者),读写兼有则改用泛型方法;误用如强转或混用读写将导致编译失败。

PECS 原则是 Java 泛型通配符使用的实践指南,核心是按参数在方法中的“角色”选通配符:往外拿数据就用 ? extends T,往里塞数据就用 ? super T。它不看类继承图,只盯操作方向——读还是写。
只读集合:用 ? extends T(Producer)
当你从集合中取元素、遍历、做计算或调用其方法,且不修改集合内容时,它是生产者。
- 允许调用
get()、iterator().next(),返回值可安全赋给T或其父类型(如Fruit f = list.get(0)) - 禁止调用
add()、set(),哪怕传new T()也不行——编译器无法确认容器实际能接受哪种子类型 - 示例:
List extends Number>可接收ArrayList<integer></integer>或LinkedList<double></double>,遍历时都能调用doubleValue(),但不能add(3.14)
只写集合:用 ? super T(Consumer)
当你向集合中添加元素、收集结果、批量归并输入,且不依赖读出的具体类型时,它是消费者。
- 允许调用
add(T)、addAll(),任意T实例(含子类对象)都能安全加入 - 禁止将
get(0)直接当T用,只能接成Object——因为容器可能是List<object></object>或List<serializable></serializable>,上界不确定 - 示例:
Collections.<integer>addAll(dest, src)</integer>中,dest声明为Collection super Integer>,这样ArrayList<number></number>和LinkedList<object></object>都能作为目标接收整数
读写都需:别用通配符,改用泛型方法
如果一个参数既要读又要写,通配符无法兼顾安全与灵活,说明设计需要调整。
- 直接使用具体类型(如
List<string></string>)或泛型方法参数(如<t> void swap(List<t> list, int i, int j)</t></t>) - 避免写类似
void process(List> list)然后又试图list.add(...)的代码——编译器会拒绝 - 标准库中的
Collections.copy(src, dest)就是典型拆分:src 是List extends T>,dest 是List super T>
常见误用与避坑点
这些错误看似合理,实则违反类型安全逻辑:
-
List extends String> list = new ArrayList(); list.add("hi");→ 编译失败。即使String是 final 类无子类,通配符规则仍生效:只读不许写 - 把
List<apple></apple>强转成List<fruit></fruit>→ 编译报错。Java 泛型不变,必须靠? extends Fruit来适配读取场景 - 用
? super T后又写Student s = list.get(0);→ 编译失败。只能声明为Object o = list.get(0);,否则可能运行时抛ClassCastException
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











