协变与逆变原理深度剖析及实例演示

在上一次https://www.cnblogs.com/webor2006/p/11279692.html中对于泛型中的协变与逆变进行了初步了解,由于这个概念是脱离开语言而且又非常之重要的,所以接下来继续对这个概念进行进一步巩固,达到一种通透领悟的境界。

这里还是从Java的角度再来看一下关于泛型协变与逆变的一个演变过程:

其错误提示为:

因为List<String>并不是List<Object>的子类型,为啥呢?这里可以采用假设法,假设这句赋值语句代码是成立的,那可以这样写:

而由于list2是指向了list,那么可以这样写:

很显然Object是不可能是String类型,在木有强制类型转换的情况下,所以说我们的这个假设是行不通的。

好,下面定义几个类来演示协变与逆变的情况,先定义三个类:一个父类,两个子类:

因为animals里面的元素接收的是Animal的子类型,当然满足要求。那接下来再来看一句代码:

看结果:

看一下错误提示:

为啥?还是采用假设法,假设此句代码是成立的,那会产生啥问题呢?由于animals里面的元素类型是Animal以及它的子类型,也就是有可能是Cat,也有可能是Dog,那我们在取元素时就会出现混乱,也就说明对于协变而言,只能对它进行读,不能进行写。

这里有一个术语:PECS:Producer Extends,Consumer Super。表示用于返回数据的生产者需要用Extends,而用于写入数据的消费者要用Super

下面来看一下逆变的情况:

逆变只能写,不能读,所以咱们可以往里写元素:

接下来试着取一下元素:

因为元素的类型是Animal及它以上的,所以从集合中拿的话很显然不一定就是Animal,那既然它里面是Animal之上的类型,是不是可以添加一个Object类型呢,试试:

不行的,Object因为不是Animal类型,所以没法往里写,既使层次体系满足Animal之上,如果改成这样写就成:

其实在Java中的数组是天然支持协变的,如何理解?下面看一下:

但是!!其实是会产生问题的,如下:

编译运行:

以上是关于Java层面关于泛型的协变和逆变的表现,下面则回到Kotlin来看下:

接下来来看下协变:

上面的代码其实相当于这两行代码:

好,接下来再来看一下逆变:

其实相当于Java的这段代码:

所以在Kotlin中:Consumer需要用in,而Producer需要用out。

原文地址:https://www.cnblogs.com/webor2006/p/11291744.html