泛型约束
fun <T : Comparable<T>> maxOf(a: T, b: T): T { if (a > b) { return a } return b } fun <T> callMax(a: T, b: T) where T : Comparable<T>, T : () -> Unit { a() b() }
泛型的型变
泛型型变分为三种
不变
没有继承关系,传什么就是什么,只有一个类型
协变: out
只读不写
父类出现的地方可以用子类代替
只读不写:
val a: Array<out Number> = Array<Int>(2, { 3 }) a[0] //可读 a[0] = 2 //ERROR
open class W<out K> { //Error 不可写 fun add(k: K) { } //可读 fun get(): K? { return null } }
父类出现的地方,可以使用代替。类似
fun main() { val b = Box<Number>() b.a(Array<Int>(2, { 5 }))//error } class Box<E> { fun a(value: Array<E>) { } }
因为 E 是 Number,所以传入 Int 肯定是错误的。修改如下:
fun a(value: Array<out E>) {}
out 代表协变,表示继承自 E 的都可以。这个时候传入 Int 就没问题了。因为 Int 是继承 Number 的。
注意:
class Box<E> { fun a(value: Array<out E>) { value[0] =1 //Error } }
逆变:in
只写不读。某些情况可以读,但是只能往外取放在 Object 中
子类出现的地方,可以使用父类代替
只写不读
val a: Array<in Int> = Array<Number>(2, { 3 }) val s = a[0] // 不能确定返回值的类型,只能使用Any类型接收 a[0] = 2
open class W<in K> { //可写 fun add(k: K) { } //返回值 K 报错,不可读 fun get(): K? { return null } }
子类出现的地方,可以使用父类代替
fun main() { val b = Box<Int>() b.b(Array<Number>(2, { 5 }))//error } class Box<E> { fun b(value: Array<E>) { } }
E 是 Int,传入的是 Number。所以报错,修改如下
fun b(value: Array<in E>) {}
in 代表逆变,表示只要是 E 的父类都可以。所以传入 Number 就没问题了。Number 是 int 的父类
注意:
fun b(value: Array<in E>) { val s = value[0] //不能确定返回值的类型,只能使用Any类型接收 }
UnsafeVariance
违法形变约束
即声明为协变的类出现逆变,或者相反
声明为不变的类接收协变或者逆变类型的参数
class Dustbin<in T> { //报错,mutableListOf 本身是协变,但是传入的是逆变,所以报错。 val list = mutableListOf<T>() fun put(t: T) { list += t } }
修改如下:
val list = mutableListOf<@kotlin.UnsafeVariance T>()//使用注解,忽略形变即可
星投影
‘*’ 可以用在变量类型的声明位置
‘*’ 可以描述一个未知的类型
‘*’ 所替换的类型在:
协变点返回泛型参数上限类型
逆变点接受泛型参数下限类型
不能直接或间接使用在属性或者函数上
对于 Foo ,其中 T 是一个具有上界 TUpper 的协变类型参数,Foo <> 等价于 Foo 。 这意味着当 T 未知时,你可以安全地从 Foo <> 读取 TUpper 的值。
对于 Foo ,其中 T 是一个逆变类型参数,Foo <> 等价于 Foo 。 这意味着当 T 未知时,没有什么可以以安全的方式写入 Foo 。
对于 Foo ,其中 T 是一个具有上界 TUpper 的不型变类型参数,Foo<*> 对于读取值时等价于 Foo 而对于写值时等价于 Foo。
out:当接收可协变的泛型参数(out T )时, * 映射的类型为 Any?
open class W<out K> { //Error /* fun add(k: K) { }*/ fun get(): K? { return null } } fun main() { val w = W<Number>() val w1: W<*> = W<Number>() val s = w1.get() //返回类型为 Any? }
下限:当接收可逆变泛型参数(in T) 时,* 映射类型为 Nothing。
注意:因为下限无法定义,所以所有的下限类型都为 Nothing
fun main() { val w1: W<*> = W<Number>() w1.add("Nothing")//报错,只支持 Nothing } open class W<in K> { fun add(k: K) { } }
泛型实现原理
伪泛型:如果使用了泛型,则会在编译时擦除类型,运行时无实际类型生成。这点和 Java一样
真泛型:C#
泛型类型无法当做真实类型
fun <T> genericMethod(t: T) { val t = T() //Error val ts = Array<T>(3){ TODO() } //Error ,Array 在编译的时候类型时确定的,不会擦除 val cls = T::class.java //Error 无法获取 class val list = ArrayList<T>() //可以 }
内联特化
如果一个函数是内联函数,那么他会在调用处执行。一旦在调用的地方执行内联函数体,我们就可以知道他的类型时确定的了。
声明如下:
inline fun <reified T> genericMethod(t: T) { val t = T() //Error val ts = Array<T>(3){ TODO() } val cls = T::class.java val list = ArrayList<T>() }
例子:
fun <T> fromJson(json: String, classOft: Class<T>): T { } fun <T> test(json: String) { fromJson(json, T::class.java)//Error }
在调用 fromJson 的时候,无法传入 T 的class。因为 T 在编译的时候会被擦除。
这个时候就可以曲线救国:使用 内联特化:
inline fun <reified T> test(json: String) { fromJson(json, T::class.java)//Success }
这个函数中的代码会被内联到调用处,所以泛型的内心自然也就晓得了。
泛型原理
泛型实现
伪类型擦除:在编译时会擦除为上限类型
真:类型生成
内联特化
扩充伪泛型的限制
案例:
一般情况下,在继承关系中,子类可以调用父类的方法,但是父类无法调用子类方法。但是通过接口和泛型可以实现调用子类方法:
typealias OnConfirm = () -> Unit typealias OnCancel = () -> Unit private val EmptyFunction = {} open class Notification( val title: String, val content: String ) class ConfirmNotification( title: String, content: String, val onConfirm: OnConfirm, val conCancel: OnCancel ) : Notification(title, content) /** * 实现 SelfType 接口。泛型是 NotificationBuild 的子类 * 注意 SelfType 有一个属性 self。是泛型的类型 * 所以 可以通过返回的 self 调用子类的方法。 */ open class NotificationBuild<Self : NotificationBuild<Self>> : SelfType<Self> { protected var title = "" protected var content = "" fun title(title: String): Self { this.title = title return self } fun content(content: String): Self { this.content = content return self } open fun build() = Notification(title, content) } //将子类类型作为泛型传给父类 class ConfirmNotificationBuilder : NotificationBuild<ConfirmNotificationBuilder>() { protected var onConfirm: OnConfirm = EmptyFunction protected var onCancel: OnCancel = EmptyFunction fun onConfirm(onConfirm: OnConfirm): ConfirmNotificationBuilder { this.onConfirm = onConfirm return this } fun onCancel(onCancel: OnCancel): ConfirmNotificationBuilder { this.onCancel = onCancel return this } override fun build(): ConfirmNotification { return ConfirmNotification(title, content, onConfirm, onConfirm) } } interface SelfType<Self> { //将 this 强转为 泛型的类型 val self: Self get() = this as Self } fun main() { //创建 ConfirmNotificationBuilder 对象时,就将当前类作为泛型传给父类了 ConfirmNotificationBuilder() .title("标题") .content("内容") .onCancel { println("OnCancel") } .onConfirm { println("OnConfirm") } .build() .onConfirm() }
泛型
基本概念:1,泛型参数,2,函数,类添加泛型
泛型约束:
泛型上限:通过 where 语句添加多个上限
泛型形变
不变:指定的泛型类型
协变:只读不写,意思是只能读取,不能写入
逆变:只写不读,某些情况可以读,但是只能是 Any 类型
泛型形变点
协变点:函数返回值类型为泛型参数类型
逆变点:函数参数为泛型参数类型
UnsafeVariance
型变点与泛型不一致是使用这个注解
UnsafeVariance
星投影
协变向上,获取上限
逆变向下,获取下限,所以类型的下限为 Nothing,不可被添加。
泛型原理和内联特化
泛型的擦除,在编译时,泛型的类型会被擦除。
调用某一个泛型方法,无法直接使用泛型,这时就可以使用内联特化。该方法会在调用处执行。泛型是明确的。
模拟Self Type
通过泛型参数拿到子类的类型,从而可以调用子类的方法