JAVA中的协变和逆变

淡淡的烟草味﹌ 2022-12-31 02:26 319阅读 0赞

协变逆变的概念

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

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

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

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

协变

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

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

逆变

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

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

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

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

设计模式

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

墨子的智慧

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

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

发表评论

表情:
评论列表 (有 0 条评论,319人围观)

还没有评论,来说两句吧...

相关阅读

    相关 JAVA

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

    相关 Typescript

    最近用TS时碰到协变和逆变的一些概念,发现有篇外国人写的文章比较容易理解的,这里记录下。 1. 协变和逆变简单理解 先简单说下协变和逆变的理解。 首先,无论协变还是逆

    相关 之疑问

    前言 关于协变和逆变已经有很多园友谈论过了,学习时也参考过园友们的文章,非常之到位!这个问题可能对您而言很简单,若有解释,请告知,在此感谢。高手绕道! 既然是标题是协变

    相关 详解

    逆变(`contravariant`)与协变(`covariant`)是`C4`新增的概念,许多书籍和博客都有讲解,我觉得都没有把它们讲清楚,搞明白了它们,可以更准确地去定义泛