layout: post
title: "kotlin-泛型"
subtitle: "古人真厉害 不管他们实际想说的是什么 限制在一行最多7个字里面"
泛型
什么是泛型?
- 泛化的类型或者说类型的抽象
- 很多时候我们并不关心它是什么
- 而关心它能做什么,这就是泛型要解决的问题.
如何为函数声明泛型
- 为函数声明泛型,泛型要放在函数名之前.
fun < T : Comparable<T>> maxOf(a: T, b: T): T {
return if (a > b) a else b
}
如何为类声明泛型
- 为类声明泛型,泛型要放在类名之后
class showText<T>(val i: T, val j: T) {
override fun toString(): String {
return "$i,$j"
}
泛型约束
-
<T : Comparable<T>>
-
:冒号之后指定的类型是上界,泛型T只能是Comparable<T>的子类.
-
若没有声明,默认的上界是Any? 在尖括号中只能指定一个上界.如果同一类型参数需要多个上界,我们需要一个单独的where子句
fun<T> maxof(a:T,b:T):T
where T:Comparable<T>,T:Cloneable {
return if (a>b) a else b
}
泛型的实现机制
- 何为真泛型(C#)
- 真泛型编译前变异后,泛型都存在
- 何为伪泛型(Java,Kotlin)
- 伪泛型只存在编译之前,编译之后就随风而去了
Kotlin对于真泛型的部分实现
- 使用 inline(内联)关键字标记方法
- 使用 Reifild(具体化)关键字标记泛型
注意泛型具体化必须使用inline标记
@Test
fun addition_isCorrect() {
Generic<Int>()
}
inline fun <reified T>Generic(){
println(T::class.java)
}
输出结果
class java.lang.Integer
型变
协变(out)
- 泛型参数为协变时,这个类型的继承关系与泛型参数的继承关系是一致的.
- kotlin中的协变(out)相当于java中的通配符 <?extends E>,表示接受 T 或者 T 的子类.
/**
* 协变
* Number是Int的父类
* List<Number>是List<Int>的父类
*/
var list:List<Number> = listOf(1,2,3)
val numlist:List<Number> = intList
逆变(in)
- 泛型参数为逆变时,这个类型的继承关系与泛型参数的继承关系是相反的.
- kotlin中的逆变(in)相当于java中的通配符 <?super E>,表示接受 T 或者 T 的超类.
/**
* 逆变
* Comparable<Int>居然是Comparable<Any>的父类我没有瞎吧?
* 你没有瞎,可是为什么会这样呢?因为是逆变呀
* Int的超类是Any
*/
val intComparable : Comparable<Int> = object : Comparable<Any>{
override fun compareTo(other: Any): Int {
return 0
}
}
/**
* 我们看下实现,Comparable<in T>
* 逆变(in) :泛型类型与实参的继承关系相反
* 这就可以解释通了
*/
public interface Comparable<in T> {
public operator fun compareTo(other: T): Int
}
注意事项
-
协变点
- 一提到协变你就应该想到类型是只读的
- 泛型参数:返回值类型
对于谁只读呢? 对于泛型参数类型对应的变量时只读
-
逆变点
- 一提到逆变你就应该想到类型是只写的
- 泛型参数:入参类型
对于谁只写呢? 对于泛型参数类型对应的变量时只写
-
不变点
- <font size="3" color="#006666">T</font>
- 类型是可读写的
-
型变点违规
- 使用 @UnsafeVariance 标注,让编译器闭嘴.
星投影
有时你对类型参数一无所知,但任然希望以安全的方式使用它.星投影就是你了.
var list:List<*> = listOf(1,2,3)
注意需要泛型实参时不能使用星投影
//这样用可以吗当然不可以!!!!
hello<*>()
fun <T> hello() {}
网友评论