登录
首页 >  文章 >  java教程

泛型通配符上界下界怎么区分?PECS实战建议

时间:2026-05-07 12:59:56 191浏览 收藏

泛型通配符的上界( extends T>)和下界( super T>)常让人混淆,其实核心在于“生产者用上界,消费者用下界”——即PECS原则:当你从集合中**读取**元素(作为生产者),应使用上界以确保类型安全;当你向集合中**写入**元素(作为消费者),应使用下界以保证兼容性。文章通过List extends Number>与List super Integer>等典型对比,结合实际场景(如拷贝、过滤、聚合)给出可立即上手的实战建议,帮你彻底理清边界选择逻辑,写出更健壮、可复用的泛型代码。

Java中的泛型通配符上界与下界怎么分_PECS原则实战建议

为什么 List 不能 add,但能 get 为 Number

因为编译器只保证「取出来的一定是 Number 或其子类」,但完全不确定底层具体是 IntegerDouble 还是 AtomicInteger —— 所以禁止写入(连 new Integer(1) 都不许),否则就可能把 Double 列表塞进一个 Integer 元素,破坏类型安全。

常见错误现象:

  • list.add(42); 编译报错:「no suitable method found for add(int)」
  • 误以为 ? extends Number 是“比 Number 更宽”,实际它是“比 Number 更窄”的读取视图

使用场景:
  • 统计所有数字集合的总和(sum += list.get(i).doubleValue()
  • 批量转换为字符串(list.stream().map(Object::toString)
  • 作为只读参数传给工具方法,比如 printAll(List animals)

为什么 List 能 add(Integer),但 get 只能赋给 Object

因为编译器只知道容器装的是 Integer 的某个父类(可能是 IntegerNumberObject),所以往里塞 Integer 安全(子类可赋值给父类引用),但取出来时,你无法确定它到底是 Number 还是 Object,只能按最上层类型 Object 处理。

典型误用:

  • Number n = list.get(0); → 编译失败,必须写成 Object o = list.get(0);
  • 试图用 ? super T 做数据提取,结果发现啥都强转不了

使用场景:
  • 收集多种整数来源到一个统一容器:比如把 IntStream 的 boxed 结果、用户输入的 Integer、解析后的 BigInteger(需先转成 Integer)都塞进 List
  • 作为 Consumer 接口的入参,例如 void acceptAll(List sink, Integer... items)

?? extends T? super T 三者参数传递时到底能不能互换

不能随意互换,编译器会按协变/逆变规则严格检查。关键看你是「往外拿」还是「往里塞」:

参数差异: