Java泛型通配符通过上界(? extends T)和下界(? super T)实现读写分离:上界保证只读安全,下界保证只写安全,遵循PECS原则(Producer-Extends, Consumer-Super)。

Java 泛型通过通配符(?)配合上界(? extends T)和下界(? super T)约束,实现对集合读取与写入操作的类型安全控制。核心不是“让泛型变灵活”,而是**用编译器可验证的规则,明确告诉它:这里只许读、那里只许写、读出的东西能当什么用、写进去的东西必须满足什么条件**。
只读场景:用 ? extends T 保证读取安全
当你需要从集合中取出元素,并统一按某个父类型处理时,就用上界通配符。
- 声明形如 List extends Number>,表示“这个列表装的是 Number 或它的任意子类(Integer、Double 等),但具体是哪一种,编译器不确定”
- ✅ 允许:Number n = list.get(0); —— 编译器知道取出来的至少是 Number
- ❌ 禁止:list.add(new Integer(1)); —— 编译器不知道底层是 List
还是 List ,加 Integer 可能破坏 Double 列表的类型一致性 - 典型用途:求和、打印、转换等只读操作,例如 double sum(List extends Number> nums)
只写场景:用 ? super T 保证写入安全
当你需要向集合中添加统一类型的对象,且希望该集合能兼容更宽泛的父类容器时,就用下界通配符。
- 声明形如 List super Integer>,表示“这个列表的实际类型是 Integer 或它的任意父类(Number、Object、Serializable 等)”
- ✅ 允许:list.add(new Integer(42)); list.add(new AtomicInteger(10)); —— 因为 Integer 和其子类都能赋值给 Number 或 Object 引用
- ❌ 禁止:list.add(new String("x")); list.add(new Object()); —— String 和 Object 不属于 Integer 的继承链,编译直接报错
- ⚠️ 注意:读取只能得到 Object,例如 Object o = list.get(0);;想转成 Integer 必须手动强转,且有 ClassCastException 风险
读写分离:PECS 原则是关键依据
PECS(Producer-Extends, Consumer-Super)不是经验口诀,而是类型系统设计的自然结果——它直接对应你对集合的使用意图。
- 如果方法参数是数据“提供方”(比如要从中 get、stream、iterate),它就是 Producer → 用 ? extends T
- 如果方法参数是数据“接收方”(比如要往里 add、accept、write),它就是 Consumer → 用 ? super T
- 工具方法常同时出现两者:例如 public static
void copy(List extends T> src, List super T> dest) ,src 只读、dest 只写,合起来才安全
无界通配符 ? 的定位很清晰
List> 不是“万能兜底”,而是“最保守读取”。它适合只调用 size()、isEmpty()、clear() 等不依赖元素类型的方法,或仅需把元素当 Object 处理的场景。
- ✅ 允许:Object o = list.get(0); list.clear();
- ❌ 禁止:list.add("abc"); list.add(new Object()); —— 除了 null,任何具体类型都不被允许
- 它比 ? extends Object 更受限,也比 ? super Object 更宽松,但实际可用性最低,慎用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











