泛型
- 泛型类型参数 class Map<K,V> 可以用具体的类型实参实例化。 IDE推到类型
listOf("Jack Ma","Bob") //IDE推导出类型泛型是String
- 泛型函数与属性

fun <T> List<T>.silent(indicate: IntRange):List<T> = listOf<T>()
//调用
val toList = ('A'..'Z').toList()
list.silent(1..3)
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T> {
return filterTo(ArrayList<T>(), predicate)
}
toList.filter{it}//这里的it就是根据toList推到出来的,在这里it => List<Char> it = Char类型
- 声明泛型类
interface List<T> {
operator fun get(index: Int): T
}
类型参数约束

- 指定多个约束
fun <T> ensureTraningPriod(seq: T) where T : Char, T : Appendable {//报错,第一个T?
}
让类型形参非空
没有指定上界的类型形参,将会使用Any?这个上界。
class procress<T>(){
fun procress(value: T){
value?.hashCode()//默认是可空类型,需要作出判断再处理
}
}
- 如果你想保证 类型形参为非空类型,可以指定一个上界
class procress<T : Any>(){
fun procress(value: T){
value.hashCode()
}
}
运行时泛型:擦除和实化类型参数
在JVM运行时候,泛型是会被擦除的。
*运行时的泛型 kotlin不允许使用没有指定泛型的类型参数
fun print(c:Collection<Int>){
if (c is List<Int>) {//书上说,这里不会报错,实际上IDE会报错?
println(c.sum())
}
}
声明带实化(实例化)类型参数的函数
fun <T> isA(value: Any) = value is T //这里会报错,在调用函数体,不能确定用的类型实参。
避免上述情况,可以使用内联函数-》inline修饰,类型参数可以实例化
inline fun <reified T> isA(value: Any) = value is T//这里编译正常,使用了内联函数,类型实参使用了reified关键字。
原理:内联函数生成字节码插入每一个调用地方,每次调用编译器都知道你需要调用类型实参的具体类
使用实化类型参数代替类引用
实化类型参数方法
- 实化类型参数转换(使用 is !is as as?)
- 使用Kotlin反射API
- 获取对应的Java.Lang.Class
- 作为调用其他方法的类型实参
泛型和子类型化
- 为什么存在变形:为了给函数传递实参 这样可以很方便调用,而且编译器在发现不安全地方会报错
类、类型和子类型
变量的类型规定了该变量的可能值,类型和类是不一样的:
- 非泛型类-》var x = String 或者 var x = String? 说明了一个Kotlin类都可以用于构造至少2种类型。
- 泛型类 -》 List List List<List> 等等
- 超类和子类是反义词
fun testPrint(i: Int) {
val num: Number = i//这里不会报错,因为Int是Number的子类
fun f(str: String) {
}
f(num)//这里会报错因为Int不是String的子类
}
- 类型和子类型是不一样的。eg: 非空类型A可以是空类型的子类,但是反过来不是。
协变:保留子类型关系

open class Animal {
fun feed() {}
}
class Herd<T : Animal> {
val size get() = 3
operator fun get(i:Int) :T = {}//定义get约定
}
fun feedAll(animals:Herd<Animal>){
for (i in 0 until animals.size)
animals[i].feed()
}
//
class Cat:Animal(){
fun clearLitter(){}
}
fun takeCareOfCat(cats:Herd<Cat>){
for (i in 0 until cats.size){
cats[i].clearLitter()
cats[i].feed()
}
feedAll(cats) //这里会报错
}
- 类型参数T上关键字的2个含义
- 子类型化会被保留
- T只能在
out位置上
List<Interface>接口。在kotlin,List是只读的,只有返回值T,没有把类型为T的元素存储在列表的方法。所以他是协变的比如:
interface ListStr<out T> : Collection<T> { //声明T为协变
operator fun get(index: Int): T //这里的类型参数只出现在返回值,所以是协变
}
不能把MutableList<T>在他类型声明成协变,因为他基友接收类型T值作为参数方法,也有返回类型T(T出现在in和out两种位置上)
需要注意构造方法中的var变量,如果用var声明变量有类型T,则不能使用out泛化
逆变:反转子类型化
逆变

interface Comparetor<in T>{ //
fun compare(e1:T,e2:T):Int {/**/}
}
in关键字的意思是:对应类型的值是传递进来给这个类的方法,并且被这个类消费的。意味着子类型化被反转,而且T只能是留在in位置

interface Fuction1<in P, out R> {
operator fun invoke(p: P): R
}
上面可以使用(P)->R表示更直观
点变形:在类型出现的地方指定变形
比如:Java中(? extends和? super)
End