PECS原则是类型安全驱动的使用约定:Producer用? extends T(只读),Consumer用? super T(只写),混合场景应避免通配符;它仅在编译期生效,不改变运行时行为。

PECS 原则不是语法糖,而是类型安全驱动下的使用约定:当你设计一个泛型方法或接口时,参数该用 ? extends T 还是 ? super T,取决于它在方法体中是“往外吐数据”还是“往里收数据”。
Producer 场景:只读不写,用 ? extends T
当方法仅从泛型参数中获取元素(即它是数据的生产者),就用上界通配符。编译器保证你能安全地把取出的元素当作 T 或其父类 使用。
- ✅ 允许操作:
get()、iterator().next()、增强 for 循环遍历 - ❌ 禁止操作:
add()、addAll()、set()(除null外) - 典型例子:
void printAll(List<? extends Number> nums)—— 可传入List<Integer>或List<Double>,内部统一按Number处理
Consumer 场景:只写不读,用 ? super T
当方法只向泛型参数中添加元素(即它是数据的消费者),就用下界通配符。编译器保证你能安全地把任意 T 类型或其子类实例 放进去。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- ✅ 允许操作:
add(T)、addAll(Collection<? extends T>) - ❌ 禁止操作:
get()返回的是 最宽上界(如 Object),不能直接当T用,除非强转 - 典型例子:
void addIntegers(List<? super Integer> target)—— 可传入List<Integer>、List<Number>或List<Object>,都能接收new Integer(42)
混合场景:既要读又要写,就别用通配符
如果一个参数既被遍历又被添加元素,说明它承担双重角色,此时通配符会限制操作或破坏安全。直接使用确定类型更清晰、更安全。
立即学习“Java免费学习笔记(深入)”;
- ❌ 避免:
void process(List<? extends String> list)再调用list.add("x") - ✅ 推荐:
void process(List<String> list)—— 类型明确,读写自由 - 真实案例:
Collections.copy(dest, src)拆成两个参数:dest 是List<? super T>(只写),src 是List<? extends T>(只读)
常见误用提醒
别把 PECS 当成“泛型继承规则”,它只约束通配符参数的使用方式;也不代表运行时行为变化——所有检查都在编译期完成。
-
List<? extends Fruit>不是List<Fruit>的子类,只是能接受List<Apple>等作为实参 - 数组支持协变(
String[]→Object[]),但泛型不支持,这是 PECS 存在的根本原因 - Comparator 参数用
? super E:因为比较器要消费E实例,比如Comparator<Animal>能比较Cat列表中的元素

















