kotlin范型中的reified关键字

深入学习Kotlin基础知识
2022-06-05 15:45 · 阅读时长4分钟
小课

今天介绍的kotlin关键字reified和之前介绍的另外两个关键字outin一样也是用来强化范型的使用。我们知道kotlin在jvm平台和java一样,范型类型在编译后会擦除,所以在运行时我们无法把范型类型当成正常的Class来使用,比如说下面这段代码时编译不通过的。

fun <T> test(t: T) {
    println(T::class.java)
}

有人可能想为什么非要获取Class呢?有些时候统一封装某些方法,但是这些方法内部就需要Class,比如Gson转化,Android页面跳转中用到的Intent。

public Intent(Context packageContext, Class<?> cls) { ... }
public <T> T fromJson(String json, Class<T> classOfT) { ... }

如果没有reified关键字或者在java中,我们如果要想在范型方法中到范型的真实类型,我们需要增加额外的参数。

fun <T> test(t: T, clazz: Class<T>) {
    println(clazz)
}

在kotlin有了更加优雅的实现,就是inline+reified,不再需要单独传一个Class参数,我们可以把test方法定义成下面这样,就可以在方法中直接使用T::class.java来得到范型的真实类型。

inline fun <reified T> test(t: T) {
    println(T::class.java)
}
inline关键字

在介绍reified之前需要先了解一下inline,它是一个用于修饰方法的关键字,使用inline修饰说明该方法是一个内联方法,内联方法在编译后会直接将内联方法体中的字节码整体编译进调用方法中,比如说有一个内联方法sayHello。

inline fun sayHello() {
    println("hello")
}

还有一个方法test,调用了sayHello方法。

fun test() {
    sayHello()
}

如果sayHello是普通方法的话,编译成字节码之后test方法中仍然发生了方法调用,但是因为sayHello是内联方法,所以编译之后,编译器会把sayHello方法体中的字节码全部编译进test方法体中,如果用源代码的形式表示的话,test方法变成了这样。

fun test() {
    println("hello")
}
reified关键字

前面提到了范型类型最终会擦除,使用Object来替代,那么reified是怎么做到还能获取到范型的真实类型的呢?首先要知道的是,使用reified来修饰方法中范型的前提是该方法是内联方法。而内联方法在编译时会编译到调用方法的方法体中,这么一来,编译器就能够确定传入内联范型方法中的真实类型,那为何还要用Object来替换呢,直接使用真实类型即可,比如说上面的test方法。

inline fun <reified T> test(t: T) {
    println(T::class.java)
}

我们再通过另外一个方法demo来调用它。

fun demo() {
    test("hello")
}

当编译器把test方法中的方法提编译进demo方法中时,已经确定传入的是String类型,那么内联的时候就能够用String类型来替换范型了,用源代码的形式来表示的话,demo方法最终编译成了这样。

fun demo() {
    println(String::class.java)
}
总结

reified关键字强化了kotlin范型的用法,需要注意的是reified关键字只能用于内联方法,也就是要和inline关键字一起使用,另外一点,包含reified关键字的内联方法在java中是无法调用的。 

kotlin范型reified