Java泛型 协变与逆变

定义:

逆变与协变用来描述类型转换(type transformation)后的继承关系,其定义:如果A、B表示类型,f(⋅)表示类型转换,≤表示继承关系(比如,A≤B表示A是由B派生出来的子类)
f(⋅)是逆变(contravariant)的,当A≤B时有f(B)≤f(A)成立;
f(⋅)是协变(covariant)的,当A≤B时有f(A)≤f(B)成立;
f(⋅)是不变(invariant)的,当A≤B时上述两个式子均不成立,即f(A)与f(B)相互之间没有继承关系。

### Java 中的逆变及类擦除 #### (Covariance) 是指当子类对象可以被当作父类对象处理时,在中也遵循这一原则。具体来说,如果 `T` 是 `S` 的子类,则 `List<T>` 可以作为 `List<S>` 使用。这主要适用于只读场景。 ```java // 正确:因为 Apple 继承自 Fruit List<? extends Fruit> fruits = Arrays.asList(new Apple()); ``` 这种特性通过通配符 `? extends T` 来实现[^1]。 #### 逆变 (Contravariance) 逆变则相反,它允许使用更广的超类来代替特定的参数。这意味着如果 `A` 是 `B` 的父类,那么 `Consumer<A>` 就能兼容 `Consumer<B>`。此功能特别适合用于写入操作。 ```java // 正确:因为 Object 是所有类的基类 void addItems(List<? super Integer> list) { list.add(1); // 合法的操作 } ``` 这里使用的通配符是 `? super T`。 #### 类擦除 (Type Erasure) Java 中的是在编译期实施的一种机制,运行时不保留具体的类信息。为了保持向后兼容性并简化虚拟机的设计,JVM 并不区分不同类实例;所有的都被视为原始类(raw type)。例如: ```java public class Box<T> { private T content; public void setContent(T t){ this.content=t; } } // 编译后的字节码实际上类似于: class Box { private Object content; public void setContent(Object o){ this.content=o; } } ``` 因此,像下面这样的代码会报错,因为它违反了静态类安全规则[^2]: ```java List<Fruit> fruitBox = new ArrayList<Apple>(); // 错误:无法转换 ``` 上述例子展示了即使 `Apple` 是 `Fruit` 的子类,也不能直接将 `ArrayList<Apple>` 赋给 `List<Fruit>` 类量。这是由于在运行时这些列表都成了普通的 `ArrayList<Object>`,从而失去了类安全性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值