Kotlin笔记22-泛型的高级特性,reified
14.1 泛型的高级特性,reified
- 对泛型进行实化
reified
Kotlin中是可以将内联函数中的泛型进行实化的
示例代码如下:
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.Stringresult2 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的子类合情合理.