简述:
Kotlin中泛型相关的文章也几乎接近尾声,但到后面也是泛型的难点和重点。相信有很多初学者对Kotlin中的泛型型变都是一知半解,比如我刚开始接触就是一脸懵逼,概念太多了,而且每个概念和后面都是相关的,只要前面有一个地方未理解后面的难点更是越来越看不懂。Kotlin的泛型比Java中的泛型多了一些新的概念,比如子类型化关系、逆变、协变、星投影的。个人认为学好Kotlin的泛型主要有这么几个步骤:
- 第一,深入理解泛型中每个小概念和结论,最好能用自己的话表述出来;
- 第二,通过分析Kotlin中的相关源码验证你的理解和结论;
- 第三,就是通过实际的例子巩固你的理解;
由于泛型型变涉及的内容比较多,所以将它分为上下两篇,废话不多说请看以下导图:
一、为什么会存在型变?
首先,我们需要明确两个名词概念: 基础类型和实参类型。例如对于List<String>
, List
就是基础类型而这里的String
就是实参类型
然后,我们需要明确一下,这里的型变到底指的是什么?
可以先大概描述一下,它反映的是一种特殊类型的对应关系规则。是不是很抽象?那就先来看个例子,例如List<String>和List<Any>
他们拥有相同的基础类型,实参类型String
和Any
存在父子关系,那么是不是List<String>
和List<Any>
是否存在某种对应关系呢? 实际上,我们讨论的型变也就是围绕着这种场景展开的。
有了上面的认识,进入正题为什么需要这种型变关系呢?来看对比的例子,我们需要向一个函数中传递参数。
fun main(args: Array<String>) {
val stringList: List<String> = listOf("a", "b", "c", "d")
val intList: List<Int> = listOf(1, 2, 3, 4)
printList(stringList)//向函数传递一个List<String>函数实参,也就是这里List<String>是可以替换List<Any>
printList(intList)//向函数传递一个List<Int>函数实参,也就是这里List<Int>是可以替换List<Any>
}
fun printList(list: List<Any>) {
//注意:这里函数形参类型是List<Any>,函数内部是不知道外部传入是List<Int>还是List<String>,全部当做List<Any>处理
list.forEach {
println(it)
}
}
上述操作是合法的,运行结果如下
如果我们上述的函数形参List<Any>
换成MutableList<Any>
会变成什么样呢?
fun main(args: Array<String>) {