天天看点

Kotlin学习之泛型的高级特性

泛型实体化

Java中的泛型是通过类型擦除机制来实现的,而Kotlin却允许将内联函数中的泛型进行实化。

要将某个泛型实化需要两个前提条件。首先,该函数必须是内联函数才行,也就是要用inline关键字来修饰该函数。其次,在声明泛型的地方必须加上reified关键字来表示该泛型要进行实化。示例代码如下:

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

然后我们就可以通过以下代码获得泛型的具体类型了:

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

运行结果如下

Kotlin学习之泛型的高级特性

泛型实体化的应用

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

对于启动一个Activity,以前我们使这样写的

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

但是Kotlin的泛型实体化让我们有了更好的选择

新建一个reified.kt

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

本来Intent接收的第二个参数应该是一个具体的Activity的Class类型,但由于T是一个被实体化的泛型,因此这里我们直接传入T::class.java。

如果我们想要启动TestActivity,只需要这样写就可以了

同时,可能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()函数时使用Lambda表达式为Intent传参

startActivity<TestActivity>(context) {
	putExtra("param1","data")
	putExtra("param2",123)
}
           

协变和逆变

在开始学习协变和逆变之前,我们还得先了解一个约定。一个泛型类或者泛型接口中的方法,它的参数列表是接收数据的地方,因此可以称它为in位置,而它的返回值是输出数据的地方,因此可以称它为out位置,如下图所示。

Kotlin学习之泛型的高级特性

协变的定义:假如定义了一个MyClass的泛型类,其中A是B的子类型,同时MyClass又是MyClass

的子类型,那么我们就可以称MyClass在T这个泛型上是协变的。

逆变的定义:假如定义了一个MyClass的泛型类,其中A是B的子类型,同时MyClass

又是MyClass的子类型,那么我们就可以称MyClass在T这个泛型上是逆变的。
Kotlin学习之泛型的高级特性

观察如下代码,我们在泛型T的声明前面加上了一个out关键字。这就意味着现在T只能出现在out位置上,而不能出现在in位置上,同时也意味着SimpleData在泛型T上是协变的。

class SimpleData<out T>(val data: T?) {
    fun get(): T? {
        return data
    }
}
           

观察如下代码,我们在泛型T的声明前面加上了一个in关键字。这就意味着现在T只能出现在in位置上,而不能出现在out位置上,同时也意味着Transformer在泛型T上是逆变的。

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

在out位置上,同时也意味着Transformer在泛型T上是逆变的。

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