泛型协变、逆变

无论是java、C#、kotlin还是scala,都会遇到泛型中的协变、逆变场景,算是泛型必学技。本文的概念、思想在其他三门语言也是通用的,学会包您一招通杀。

概念

假设

  • A、B表示类型
  • ≤ 表示继承关系
  • f<⋅>表示类型转换

若A ≤ B,则 A是B的子类,B是A的超类

协变、逆变

什么是型变?型变(type variance)允许对类型进行子类型转换。

为了下面讲解先定义几个类:

static class Animal {}
static class Human extends Animal {}
static class Wolf extends Animal {}
static class Man extends Human {}

1. 协变(covariant)

当A≤ B有 f<A> ≤f <B>

List<? extends Animal> list=new ArrayList<Human>();

子类通配符(也叫上界通配符)<? extends T>

规定了元素的根类是Animal,可get出Animal类型

Animal animal=list.get(0);

但不能add,继承自Animal的类型可以是Human也可以Wolf,若添加Wolf肯定出错所以编译器禁止

list.add(new Human());
list.add(new Wolf());

java中的数组默认支持协变的

Animal []array=new Animal[3];
array[0]=new Human();
array[1]=new Wolf();
array[2]=new Man();

2. 逆变(contravariant)

当A≤B有 f<B>≤f<A>

List<? super Human> list=new ArrayList<Animal>();

超类通配符(也叫下界通配符)<? super T>

规定了元素的超类是Human,所以可以add Human类型及其子类型

list.add(new Human());
list.add(new Man());

不可以 get Human类型,因为没明确指定统一的“根”(除Object),只能get出Object类型

Human h=list.get(0);

3. 不变(invariant)

当A≤B时上述协变、逆变均不成立

List<Object> list=new ArrayList<String>();

Java的泛型默认是不变的(在不使用上界/下界通配符的情况下)

生产者协变,消费者逆变

哪么实际使用中何时协变何时逆变呢?《Effective Java》总结好了:producer-extends, consumer-super(PECS)

  • 读取(get)的对象称为生产者(Producer),可从生产者中安全地读取;
  • 写入(add)的对象称为消费者(Consumer) ,可对消费者写入该类型和字类型(Human和Human的子类Man)。

转载于:https://my.oschina.net/droidwolf/blog/1930934

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值