什么是协变, 逆变?

2019-02-19  本文已影响0人  随风风筝

好的软件的作用是让复杂的东西看起来简单。

java中协变跟逆变是对泛型类的继承关系的表述.
如:
List<Number>List<Integer> 之间是没有继承关系的.
但是直观上会觉得, IntegerNumber 的子类, 所以List<Integer> 应是List<Number> 的子类.
如果想要这种效果, 就要用协变.
List<? extends Number> 这样 List<Integer> 就能成为List<? extends Number> 子类, 也就是可以赋值

List<Integer>b = new ArrayList<>();
List<? extends Number> a = b;

这里如果你想要相反的效果, 则用逆变,List<? super Number> 这样继承关系就会相反.

那么什么时候用协变,逆变?
协变主要是用在函数的返回值上,逆变用在函数参数上,这样的规则也就能遵循里氏替换原则.
Function, 在这里R 作为函数的返回值, 所以这个泛型要协变, 而T用在函数的参数上所以要用逆变

Function<? super Dog,? extends Animal> f1;

这里举个例子
假设有以下继承关系:
车 > 轿车 > 标准轿车 > 高级轿车
现在有一个人声称自己能修理所有的标准轿车, 所以发出了以下公告:

修理(List<标准轿车> cars)

假设我现在有List<轿车>List<高级轿车>
那么这个人到底能修理哪个呢? 从上面的函数声明来看都不可以.

再来看看这个人的声明
他说能够修理所有标准轿车
那么因为标准轿车扩展了轿车, 所以如果能够修理标准轿车, 那么应当可以修理轿车
所以这个函数应当可以接受所有标准轿车的父类
也就是说 List<轿车> 能够传入 以List<标准轿车>为参数的函数
换句话说 List<轿车>List<标准轿车>的子类, 这样才能传入参数
所以上面的公告要用逆变, 改成如下:

修理(List<? super 标准轿车> cars)

也许也不会有人想把自己的高级轿车交给这家伙.
以此类推, 函数的返回值应当用协变, 这样既能满足里氏替换原则

上一篇 下一篇

猜你喜欢

热点阅读