美文网首页
协变和逆变

协变和逆变

作者: 俊杰的笔记 | 来源:发表于2017-05-18 16:03 被阅读72次

Java的泛型只有通配符?和extends、super,没有语法上的协变和逆变。

什么是协变和逆变?

在混合OO和多态性时,一个核心问题是:如果T’是T一个子类,Container[T’]应该被看做是Container[T]的子类吗?Java中是不允许的。List<String>和List<Object>无关。

协变:covariant,C[T’]是 C[T] 的子类,
在Java中,List<Integer>是List<? extends Number>的子类。
协变很容易理解。一个方法需要List<? extends Number>参数时,可以传递一个List<Integer>.
又如:

Holder<? extends Animal> birdHolder = new Holder<Bird>(new Bird()); // 编译OK

由于Java并没有协变,所以写成下面的样子是错误的,编译不通过。

Holder<Animal> birdHolder = new Holder<Bird>(new Bird()); //编译不通过

逆变:contravariant,C[T] 是 C[T’]的子类
在Java中,List<Number>是List<? super Integer>的子类。
又如:

Holder<? super Bird> animalHolder = new Holder<Animal>(new Animal());

由于Java没有逆变,所以写成下面是错误的。

Holder<Bird> animalHolder = new Holder<Animal>(new Animal());// 编译不通过

逆变似乎很难理解。那么逆变用在什么地方呢?
在Java 8以前,? super主要用在一些类的比较方法上;
Java 8中,实际上Function的第一个参数经常采用逆变(? super T).

Function<Animal, String> a = (Animal animal) -> ThirdParty.makeSound(animal);
Function<Bird, String> func = a; // 编译错误;两个Function没有关系。

使用super逆变,使Function<Animal,String>成为Function<? super Bird, String>的子类型。

Function<Animal, String> a = (Animal animal) -> ThirdParty.makeSound(animal);
Function<? super Bird, String> func = a;

协变和逆变本质上体现的还是Liskov替换原则(Liskov substitution principle), 即“子类可以替换父类”

不变invariant:C[T] 和 C[T’]无关
不变是Java中默认的机制。List<Bird>和List<Animal>没有关系。

相关文章

网友评论

      本文标题:协变和逆变

      本文链接:https://www.haomeiwen.com/subject/ywusxxtx.html