Kotlin 之泛型详解

泛型约束

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
        }
    }
    

    父类出现的地方,可以使用代替。类似<? extends T>

    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

      • 通过泛型参数拿到子类的类型,从而可以调用子类的方法
原创文章 119 获赞 77 访问量 3万+

猜你喜欢

转载自blog.csdn.net/baidu_40389775/article/details/105402945