实化类型参数允许你在运行时的内联函数中引用作为类型实参的具体类型(对普通的类和函数来说,这样行不通,因为类型实参在运行时会被擦除)。
声明点变型可以说明一个带类型参数的泛型类型,是否是另一个泛型类型的子类型或者超类型,它们的基础类型相同但类型参数不同。例如,它能调节是否可以把List<Int>类型的参数传给期望List<Any>的函数。使用点变型在具体使用一个泛型类型时做同样的事,达到和java通配符一样的效果。
一、泛型类型参数
泛型允许你定义带类型形参的类型。当这种类型的实例被创建出来的时候,类型形参被替换成称为类型实参的具体类型。例如,如果有一个List类型的变量,弄清楚这个列表中可以存储哪种事物是很有意义的。类型形参可以准确清晰地进行描述,就像这样“这个变量保存了字符串列表”,而不是“这个变量保存了一个列表”。kotlin说明“字符串列表”的语法和java看起来一样:List<String>。还可以给一个类声明多个类型形参。例如,Map类型就有键类型和值类型两个类型形参:class Map<K, V>。我们可以用具体的类型实参来实例化它:Map<String, Person>。目前,所有概念都和java没什么不一样。
和一般类型一样,Kotlin编译器也常常能推导出类型实参:
val authors = listOf("Dmitry", "Svetlana")
因为传给listOf函数的两个值都是字符串,编译器能推导出你正在创建一个List<String>。另一方面,如果你想创建一个空的列表,这样就没有任何可以推导出类型实参的线索,你就得显式地指定它(类型实参)。就创建列表来说,既可以选择在变量声明中说明泛型的类型,也可以选择在创建列表的函数中说明类型实参。参看下面的例子:
1、泛型函数和属性
如果要编写一个使用列表的函数,希望它可以在任何列表(通用的列表)上使用,而不是某个具体类型的元素的列表,需要编写一个泛型函数。泛型函数有它自己的类型参数。这些类型形参在每次函数调用时都必须替换成具体的类型实参。
大部分使用集合的库函数都是泛型的。来看看slice函数:这个函数返回一个只包含在指定下标区间内的元素。
接收者和返回类型用到了函数的类型形参T,它们的类型都是List<T>。当你在一个具体的列表上调用这个函数时,可以显式地指定类型的实参。但大部分情况下你不必这样做,因为编译器可以推导出类型。
这两次调用的结果都是List<Char>。编译器把函数返回类型List<Char>中的T替换成了推导出来的类型Char。
再来看看filter函数的声明,它接收了一个函数类型(T)-> Boolean的参数。
这个例子中自动生成的lambda参数it的类型是String。编译器必须把它推导出来:毕竟,在函数声明中lambda参数是泛型类型T。编译器推断T就是String,因为它知道函数应该是list<T>上调用,而它的接收者readers的真实类型是List<String>。