泛型
- 泛型类型参数 class Map<K,V> 可以用具体的类型实参实例化。 IDE推到类型
listOf("Jack Ma","Bob") //IDE推导出类型泛型是String复制代码
- 泛型函数与属性
funList .silent(indicate: IntRange):List = listOf ()//调用val toList = ('A'..'Z').toList()list.silent(1..3)public inline fun Iterable .filter(predicate: (T) -> Boolean): List { return filterTo(ArrayList (), predicate)}toList.filter{it}//这里的it就是根据toList推到出来的,在这里it => List it = Char类型复制代码
- 声明泛型类
interface List{ operator fun get(index: Int): T}复制代码
类型参数约束
因为Int、Double都继承自Number,所以可以拥有sum()方法。一旦指定了上界,就可以在方法中调用上界的方法。- 指定多个约束
funensureTraningPriod(seq: T) where T : Char, T : Appendable { //报错,第一个T?}复制代码
让类型形参非空
没有指定上界的类型形参,将会使用Any?这个上界。
class procress(){ fun procress(value: T){ value?.hashCode()//默认是可空类型,需要作出判断再处理 }}复制代码
- 如果你想保证 类型形参为非空类型,可以指定一个上界
class procress(){ fun procress(value: T){ value.hashCode() }}复制代码
运行时泛型:擦除和实化类型参数
在JVM运行时候,泛型是会被擦除的。
*运行时的泛型 kotlin不允许使用没有指定泛型的类型参数
fun print(c:Collection ){ if (c is List ) { //书上说,这里不会报错,实际上IDE会报错? println(c.sum()) }}复制代码
声明带实化(实例化)类型参数的函数
funisA(value: Any) = value is T //这里会报错,在调用函数体,不能确定用的类型实参。复制代码
避免上述情况,可以使用内联函数-》inline修饰,类型参数可以实例化
inline funisA(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{ val size get() = 3 operator fun get(i:Int) :T = {}//定义get约定}fun feedAll(animals:Herd ){ for (i in 0 until animals.size) animals[i].feed()}//class Cat:Animal(){ fun clearLitter(){}}fun takeCareOfCat(cats:Herd ){ 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: Collection { //声明T为协变 operator fun get(index: Int): T //这里的类型参数只出现在返回值,所以是协变}复制代码
不能把MutableList<T>
在他类型声明成协变,因为他基友接收类型T值作为参数方法,也有返回类型T(T出现在in
和out
两种位置上) 需要注意构造方法中的var变量,如果用var声明变量有类型T,则不能使用out
泛化
逆变:反转子类型化
逆变
interface Comparetor{ // fun compare(e1:T,e2:T):Int { /**/}}复制代码
in
关键字的意思是:对应类型的值是传递进来给这个类的方法,并且被这个类消费的。意味着子类型化被反转,而且T只能是留在in
位置
interface Fuction1{ operator fun invoke(p: P): R}复制代码
上面可以使用(P)->R表示更直观
点变形:在类型出现的地方指定变形
比如:Java中(? extends和? super)
End