0
点赞
收藏
分享

微信扫一扫

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


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

14.1 泛型的高级特性,reified

  • 对泛型进行实化

reified

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

  1. 声明函数必须是内联函数(inline)
  2. 在声明泛型的地方必须加上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的子类合情合理.


举报

相关推荐

0 条评论