当前位置 : 主页 > 编程语言 > java >

【Kotlin】笔记22-泛型的高级特性

来源:互联网 收集:自由互联 发布时间:2022-06-30
Kotlin笔记22-泛型的高级特性,reified 14.1 泛型的高级特性,reified 对泛型进行实化 reified Kotlin中是可以将内联函数中的泛型进行实化的 声明函数必须是内联函数(inline) 在声明泛型的地方必须


Kotlin笔记22-泛型的高级特性,reified

14.1 泛型的高级特性,reified

  • 对泛型进行实化

reified

Kotlin中是可以将内联函数中的泛型进行实化的

  • 声明函数必须是内联函数(inline)
  • 在声明泛型的地方必须加上reified关键字来表示该泛型要进行实化
  • 示例代码如下:

    inline fun <reified T> getGenericType() = T::class.java

    这里实现了个Java中完全不可能实现的功能: geGenericType()
    函数直接返回了当前指定泛型的实际类型T.Class这样的语法在Java中是不合法的,而在Kotlin
    中,借助泛型实化功能就可以使用T::class.java这样的语法了.

    ​​举个栗子:​​

    fun main() {
    val result1 = genericType<String>()
    val result2 = genericType<Int>()
    println("result1 is $result1")
    println("result2 is $result2")
    }

    ​​结果:​​

    result1 is class java.Lang.String
    result2 is class java.Lang.Integer
    • 泛型实化的应用

    序号

    Tips

    1

    泛型实化功能允许我们在泛型函数当中获得泛型的实际类型,这也就使得类似于a is T, T::class.java 这样的语法成为了可能.

    ​​举个栗子:​​

    val intent = Intent(context, TestActivity::class.java)
    context.startActivity(intent)

    ​​优化:​​

    inline fun <reified T> startActivity(context: Context) {
    val intent = Intent (context,T::class.java)
    context.startActivity(intent)
    }

    ​​使用:​​

    startActivity<TestActivity>(context)

    ​​举个栗子2:​​

    通常在启用Activity的时候还可能会使用Intent附带些参数

    val intent = Intent(context, TestActivity::class.java)
    intent.putExtra("param1", "data")
    intent.putExtra("param2", 123)
    context.startActivity(intent)

    ​​优化:​​

    inline fun <reified T> startActivity(context: Context, block: Intent.() -> Unit) (
    val intent = Intent(context, T::class.java)
    intent.block()
    context.startActivity(intent)
    }

    startActivity() 函数中增加了一个函数类型参数, 并且它的函数类型是定义在Intent 类当中的. 在创建完Intent的实例之后,随即调用该函数类型参数, 并把Intent的实例传入, 这样调用startActivity()函数的时候就可以在Lambda表达式中为Intent传递参数:

    startActivity<TestActivity>(context) {
    putExtra("param1", "data")
    putExtra("param2", 123)
    }
    • 泛型的协变

    泛型类或者泛型接口中的方法: 它的参数列表是接收数据的地方, 因此可以称它为in位置, 而它的返回值是输出数据的地方, 因此可以称它为out位置

    interface MyClass<T>{
    fun method(param: T in位置 ): T out位置
    }

    in: 泛型的协变: 假如定义了一个MyClass的泛型类, 其中A是B的子类型, 同时MyClass < A>又是MyClass< B>的子类型, 那么我们就可以称MyClass在T这个泛型上是协变的.

    ​​举个栗子:​​

    class SimpleData<T> {
    private var data: T? = null

    fun set(t: T?) { data = t }
    fun get(): T? { return data }
    }

    ​​修改:​​

    class SimpleData<out T>(val data: T?) (
    fun get(): T? {
    return data
    }
    • 泛型的逆变

    out 定义: 假如定义了一个MyClass< T>的泛型类, 其中A是B的子类型, 同时MyClass< B>又是MyClass< A>的子类型, 那么我们就可以称MyClass在T这个泛型上是逆变的.

    协变和逆变的区别

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y22pjIuy-1649926891761)(C:\Users\mozhimen\AppData\Roaming\Typora\typora-user-images\image-20220414162350277.png)]

    ​​举个栗子:​​

    interface Transformer<T> {
    fun transform(t: T): String
    }

    fun main() {
    val trans = object : Transformer<Person> {
    override fun transform(t: Person): String {
    return "${t.name} ${t.age}"
    }
    }
    handleTransformer(trans) //这行代码会报错
    }

    fun handleTransformer(trans: Transformer<Student>) {
    val student = Student("Tom", 19)
    val result = trans.transform(student)
    }

    这段代码在调用handleTransformer()方法的时候却会提示语法错误, 这里Transformer并不是 Transformer的子类型. 那么这个时候逆变就可以派上用场了, 它就是专门用于处理这种情况的。修改Transformer接口中的代码:

    ​​优化:​​

    interface Transformer<inT> {
    fun transform(t: T): String
    }

    @UnsafeVariance注解可以打破, 让泛型在协变时只出现在out位置, 逆变时只出现在in位置这一语法
    规则, 但同时也会带来额外的风险, 所以你在使用@UnsafeVariance注解时, 必须很清楚自己在干什么才行.

    • 逆变功能在Kotlin内置API中的应用

    Comparable 是一个用于比较两个对象大小的接口:

    ​​举个栗子:​​

    interface Comparable<in T> {
    operator fun compareTo(other: T): Int
    }

    Comparable在T这个泛型上就是逆变的, compareTo()方法则用于实现具体的比较逻辑. 那么这里为什么要让Comparable 接口是逆变的呢?想象如下场景,如果我们使用Comparable实现了让两个Person对象比较大小的逻辑,那么用这段逻辑去比较两个Student对象的大小也一定是成立的, 因此让Comparable成为Comparable的子类合情合理.


    网友评论