泛型extends和super的区别

在 Java 泛型中,extends 和 super 是用于限定类型边界的两个关键字。它们的核心区别在于‌限定的方向‌(上界 vs 下界)以及由此决定的‌读写权限‌(Producer vs Consumer)。

理解这一区别的最佳方式是遵循 ‌PECS 原则‌:‌P‌roducer-‌E‌xtends, ‌C‌onsumer-‌S‌uper。

  1. 核心对比总结
  2. 详细解析:<? extends T> (上界)
    ‌定义‌:表示未知的具体类型,但该类型必须是 T 或者 T 的子类。

‌为什么只能读(Producer)?‌

假设你有一个 List<? extends Number>,它可能实际上是 List,也可能是 List。

‌读取安全‌:无论它是 Integer 还是 Double,它们都是 Number 的子类。因此,当你从中取出元素时,你可以安全地将其视为 Number 类型。

‌写入不安全‌:如果你尝试 list.add(1) (Integer),但实际列表是 List,就会破坏类型安全。反之亦然。因为编译器无法确定具体的子类类型,所以禁止添加任何非 null 元素。

代码示例:

java 复制代码
public void processNumbers(List<? extends Number> list) {
    // ✅ 安全读取:所有元素至少是 Number
    for (Number num : list) {
        System.out.println(num.doubleValue());
    }
    
    // ❌ 编译错误:不能确定具体类型,禁止添加
    // list.add(1); 
}
  1. 详细解析:<? super T> (下界)
    ‌定义‌:表示未知的具体类型,但该类型必须是 T 或者 T 的父类。

‌为什么只能写(Consumer)?‌

假设你有一个 List<? super Integer>,它可能实际上是 List,List 或 List。

‌写入安全‌:无论列表的具体类型是什么,它一定是 Integer 的父类(或本身)。因此,向其中添加 Integer 对象永远是安全的(Integer 可以向上转型为 Number 或 Object)。

‌读取受限‌:当你从列表中取出元素时,编译器只知道它是 Integer 的某个父类。可能是 Integer,也可能是 Number,甚至是 Object。为了类型安全,编译器只能保证取出的对象是 Object 类型,无法直接当作 Integer 使用。

‌代码示例‌:

java 复制代码
public void addIntegers(List<? super Integer> list) {
    // ✅ 安全写入:Integer 可以存入 Integer, Number, Object 列表
    list.add(10);
    list.add(20);
    
    // ⚠️ 读取受限:只能当作 Object 读取
    Object obj = list.get(0); 
    // ❌ 编译错误:不能保证取出的就是 Integer
    // Integer num = list.get(0); 
}
  1. PECS 原则实战应用
    PECS 原则帮助你在设计 API 时决定使用哪种通配符:

‌如果你需要从参数中获取数据(生产数据)‌,使用 extends。

例如:Collections.max(Collection<? extends T>) ------ 它需要遍历集合并比较元素,不需要修改集合。

‌如果你需要向参数中放入数据(消费数据)‌,使用 super。

例如:Collections.copy(List<? super T> dest, List<? extends T> src) ------ 目标列表 dest 需要接收来自源列表的数据,所以是消费者,用 super;源列表 src 提供数据,所以是生产者,用 extends。

  1. 常见误区澄清

‌ vs <? extends Number>‌:

用在‌类或方法定义‌中,表示一个确定的但未知的类型 T。在方法内部,你可以既读又写(只要类型一致),因为 T 是固定的。
<? extends Number> 用在‌变量声明或方法参数‌中,表示一个未知的类型范围。它主要用于提高 API 的灵活性,允许传入更多类型的实参,但牺牲了读写能力。 ‌null 的特殊性‌: null 是所有引用类型的成员。因此,无论是 extends 还是 super,都可以向集合中添加 null。 总结口诀 ‌Extends 上界,只读不写‌(因为不知道具体是哪个子类,怕写错)。 ‌Super 下界,只写不读‌(因为不知道具体是哪个父类,读出来只能用 Object)。

相关推荐
腻害兔1 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
维天说7 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json
Zane19947 小时前
并发 vs 并行:别再傻傻分不清了,一文讲透 Java 并发编程的第一课
java·后端
噢,我明白了8 小时前
java中Excel的导入和导出(EasyExcel)
java·开发语言·excel
吠品8 小时前
Zabbix Web界面误报Server未运行的排查与解决
java·服务器·数据库
啊湘8 小时前
天气查询API接口 按月Token鉴权 实时天气 物联网可用 文档齐全
java·后端·struts
Java内核笔记9 小时前
告别十亿美元的错误 : Spring Boot 4 空安全 (JSpecify) 实战
java·spring boot·后端