泛型实体化
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")
}
运行结果如下
泛型实体化的应用
泛型实体化的功能允许我们在泛型函数当中获得泛型的实际类型,也就是的 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位置,如下图所示。
协变的定义:假如定义了一个MyClass的泛型类,其中A是B的子类型,同时MyClass又是MyClass
的子类型,那么我们就可以称MyClass在T这个泛型上是协变的。逆变的定义:假如定义了一个MyClass的泛型类,其中A是B的子类型,同时MyClass
又是MyClass的子类型,那么我们就可以称MyClass在T这个泛型上是逆变的。观察如下代码,我们在泛型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
}