在上一次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。