一、子类与子类型;
(1)通常B类继承A类,B就是A的子类。他们两者是一种继承关系,因为父类A,子类B。(子类)
(2)在需要使用到A类变量时,可以使用B类变量来替代,B就是A的子类型,表达的不一定是继承关系,而一定包含了替代关系。(子类型)
(3)子类一定是父类的子类型。两者构成了子类型关系,但是不一定就是继承关系。继承关系必然产生一个子类型关系。
【案例】
Animal.kt
Dog.kt
MainActivity.kt
日志:
二、协变;
covariant
场景:
通常B类型是A类型的子类型,但是默认情况下,XXX<B>不构成XXX<A>的子类型。此时可以通过添加关键词out将XXX<B>变成XXX<A>的子类型。这种操作就是协变。
问题:
(1)没有协变是什么样子
(2)协变解决了什么
(3)协变的意义
【案例】
日志:
【案例 2】
A.kt
B.kt
MainActivity.kt
日志:
协变的前提条件:泛型参数一定是A的子类或本身。(小于等于A)
【案例 3】
C.kt
D.kt
Container.kt
MainActivity.kt
按钮1日志:
按钮2日志:
【注意】为什么在协变操作的内部不能给协变的子类型进行写入操作?
三、逆变;
contravariance
如果B是A的子类,默认情况下XXX<A>不是XXX<B>的子类型,通过使用关键字in,可以使用XXX<A>构成XXX<B>的子类型,这种操作叫逆变。
需要思考的问题:
(1)没有逆变是什么样子的
(2)逆变帮助我们解决了什么问题
(3)逆变的意义:
为了保证传入XXX<B>(例如ArrayList<in B>)(集合)中的元素实例必须要大于等于B这个类型才是安全的。因为后期需要操作的是对这个集合的写入操作,写入的东西必须是以类型B为上限。不能突破上限,这才是安全的。
【案例】
A.kt
B.kt
MainActivity.kt
日志:
【案例 2】
泛型的使用可以看成一个容器携带若干个元素,元素类型是泛型参数。使用逆变,让原来不是子类型的容器,构成子类型。
Container.kt
MainActivity.kt
日志:
注意:
逆变的前提条件:要实现逆变操作,必须泛型的实际值要构成继承关系。
例如:ArrayList<B>要经过逆变支持ArrayList<A>的传入,则必须B是A的子类。
【案例 3】
B.kt
C.kt
D.kt
MainActivity.kt
【注意】
协变与逆变的相关问题
A.kt
B.kt
C.kt
D.kt
Container.kt
MainActivity.kt