? extends t只读、? super t只写,遵循pecs原则:producer extends、consumer super;前者允许安全读取为t及其父类,禁止add(除null);后者允许安全add t及子类,读取仅得object。

泛型上限(? extends T)和下限(? super T)不是为了描述“类型多大”或“继承多深”,而是为了明确这个泛型容器在当前上下文中能做什么操作——是拿来取数据,还是用来塞数据。PECS 就是把这种分工浓缩成一句可落地的口诀。
上限 ? extends T:只读,安全取值
表示“某个未知类型,但它是 T 或 T 的子类”。编译器只知道它比 T 更具体,所以能保证从里面 get 出来的每个元素至少是 T 类型,可以放心调用 T 的方法。
- ✅ 允许:遍历、get()、赋值给 T 或其父类型(如 Number n = list.get(0))
- ❌ 禁止:add()、set()(除了 null),因为实际容器可能是 ArrayList
,加 Integer 会破坏类型一致性 - ? 举例:List extends Number> 可以接收 ArrayList
、LinkedList ,但不能往里 add 任何具体数字实例
下限 ? super T:只写,安全注入
表示“某个未知类型,但它是 T 或 T 的父类”。编译器只知道它比 T 更宽泛,所以任何 T 实例(包括子类)都能安全放进这个容器。
- ✅ 允许:add()、addAll(),传入 T 或其子类对象(如 add(new Dog()) 到 List super Animal>)
- ❌ 禁止:直接将 get() 结果当作 T 使用(如 Integer i = list.get(0)),只能接成 Object,因为实际可能是 List
- ? 举例:Collections.copy(dest, src) 中 dest 参数就是 super T>,确保 src 里的每个 T 都能放进 dest
PECS 原则:按角色选,不看继承方向
别被“上界/下界”字面带偏。真正决定用哪个的,是你在方法里怎么用这个参数:
- 如果你从中拿东西出来用(比如求和、打印、校验、转流)→ 它是生产者(Producer)→ 用 ? extends T
- 如果你往里塞东西进去存(比如收集、归并、填充、过滤后写入)→ 它是消费者(Consumer)→ 用 ? super T
- 如果既读又写,大概率说明接口设计过载,应拆分职责或改用具体类型 T
常见误区提醒
这些错误高频出现,但编译报错信息常让人困惑:
- List extends String> 不允许 add() —— 即使 String 是 final 类,通配符逻辑依然生效:只读不写
- List super Integer> 能 add(new Long(1)) 吗?不能。super 只放开写入 T 及其子类,Long 和 Integer 是兄弟类,不满足 IS-A 关系
- Stream.collect(Collectors.toList()) 返回 List
,而 collect(toCollection(ArrayList::new)) 的参数是 Supplier ,内部用的就是 ? super T 来兼容各种目标集合











