变型

描述了拥有相同基础类型和不同类型类型实参的泛型类型之间是如何关联;
比如 List 和 List 之间是如何关联。

协变

Kotlin 中的 List 接口表示的都是 只读 集合。如果A是B的子类型,那么List 就是 List的子类型。
这样的类火灾接口被称为协变的,就说 子类型化被保留了。协变类的前提是他必须是一个 泛型类。、

用关键字out来声明 类型参数T为协变:

  • 子类型化会被保留(Producer是Producer的子类型)

  • T只能在out位置

  • out位置表示 生产类型

  • in位置表示 消费类型

泛型-变型 - 图1
List和MutableList协变不同举例:
泛型-变型 - 图2
泛型-变型 - 图3
注意有个特例就是 构造方法的参数既不在 in位置 也不在 out位置。即使类型参数声明成了 out,也可在构造函数的声明中使用:
泛型-变型 - 图4

逆变

可以看出是协变的镜像:对一个逆变来说,他的子类化关系与用作类型实参的类的子类型化关系是相反的。即在 in位置 使用T,接口只是消费类型为T的值
泛型-变型 - 图5
泛型-变型 - 图6
泛型-变型 - 图7

一个类可以在一个类型参数上协变,同时在另外一个类型参数上逆变。Function接口就是例子
泛型-变型 - 图8

使用点变型

在类声明的时候就能够指定 变型修饰符,因为这些修饰符会应用到所有类被使用的地方-称为声明点变型。雷士用java的通配符类型(? extentds 和 ? super)。 在每一次使用类型参数的类型的时候,指定这个类型参数是否可以用它的子类型或者超类型替换,这就是使用点变型。
泛型-变型 - 图9
在Kotlin中提供了一个更优雅的方式:
泛型-变型 - 图10
如上source只用到了out位置的获取数据情况,因此定义为out,就不能执行添加数据的相关操作,因为添加数据是 destination 在此函数中才执行的操作。

可以为类型声明中类型参数任意的用法指定变型修饰符,包括形参类型,局部变量类型,函数返回类型等,这就成为 类型投影

如果类型参数已经有out类型,获取他的out投影就没有意义。比如List 这样,他表示的意识和List一样,因为List本身就是不可变类型,只能产生数据,因此定义out是多余。同理,可以对类型参数的用法使用in修饰符,表示这个特定的地方相应的值担当消费者。
泛型-变型 - 图11

泛型-变型 - 图12

星号投影

星号投影用来表示你不知道关于泛型参数的任何信息。语法很简单,只能用在对泛型类型实参不感兴趣的地方:只是使用生产值,而不关心类型。

注意MutableList<>和Mutable不一样,前者包含某种特性类型的元素的列表,只是你不知道是哪个类型,不能往里面写入任何东西,只能读取(这就类似out投影了)。而后者表示是包含任意类型的元素。Kotlin中的 MyType<>对应java中的MyType<?>
泛型-变型 - 图13