JAVA中的协变和逆变

协变逆变的概念

可变性是以一种类型安全的方式,将一个对象当做另一个对象来使用。如果不能将一个类型替换成为另一个类型,那么这个类型就称之为:不变量。

协变:如果某个返回的类型可以由其派生类替换,那么这个类型就是支持协变的。

逆变:如果某个参数类型可以由其基类替换,那么这个类就是支持逆变的。

如Function, 在这里R 作为函数的返回值, 所以这个泛型要协变, 而T用在函数的参数上所以要用逆变。

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

协变

如:List<Number>和List<Integer>之间是没有继承关系的。

但是直观上会觉得,Integer是Number的子类, 所以List<Integer>应是List<Number>的子类。

如果想要这种效果, 就要用协变。

List<? extends Number>这样List<Integer>就能成为List 子类, 也就是可以赋值。

List<Integer>b = new ArrayList<>();

List<? extends Number> a = b; 

逆变

假设有以下继承关系:

车 > 轿车 > 标准轿车 > 高级轿车

现在有一个人声称自己能修理所有的标准轿车, 所以发出了以下公告:

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

假设我现在有List<轿车>和 List<高级轿车>

那么这个人到底能修理哪个呢? 从上面的函数声明来看都不可以.

再来看看这个人的声明

他说能够修理所有标准轿车

那么因为标准轿车扩展了轿车, 所以如果能够修理标准轿车, 那么应当可以修理轿车

所以这个函数应当可以接受所有标准轿车的父类

也就是说 List<轿车> 能够传入 以List<标准轿车>为参数的函数

换句话说 List<轿车> 是List<标准轿车>的子类, 这样才能传入参数

所以上面的公告要用逆变, 改成如下:

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

设计模式

里氏替换原则:的内容可以描述为: “派生类(子类)对象可以在程式中代替其基类(超类)对象。”

墨子的智慧

《墨子:小取》中说,“白马,马也;乘白马,乘马也。骊马,马也;乘骊马,乘马也”。文中的骊马是黑的马。意思就是白马和黑马都是马,乘白马或者乘黑马就是乘马。在面向对象中我们可以这样理解,马是一个父类,白马和黑马都是马的子类,我们说乘马是没有问题的,那么我们把父类换成具体的子类,也就是乘白马和乘黑马也是没有问题的,这就是我们上边说的里氏替换原则。

墨子同时还指出了反过来是不能成立的。《墨子:小取》中说:“娣,美人也,爱娣,非爱美人也”。娣是指妹妹,也就是说我的妹妹是没人,我爱我的妹妹(出于兄妹感情),但是不等于我爱美人。在面向对象里就是,美人是一个父类,妹妹是美人的一个子类。哥哥作为一个类有“喜爱()”方法,可以接受妹妹作为参量。那么这个“喜爱()”不能接受美人类的实例,这也就说明了反过来是不能成立的。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容