Kotlin Reified Types in Inline Functions
翻译自:Kotlin Reified Types in Inline Functions
我发现很多人没有听说过 reified
类型,或是见过但是还不太理解他到底能干嘛。这篇文章可以带领你在认识 reified
类型的黑暗过程中找到一丝曙光。
Starting situation
fun <T> myGenericFun(c: Class<T>)
在一般的泛型函数中,就像myGenericFun
,你不能使用T
这个类型,因为在java中运行时它会被抹去。当然如果你想使用这个泛型作为一个普通的实例在函数体内使用,你 必须显式的作为一个参数传入,就像我上面的例子里面的c
。 这样完全能正常的跑起来,但是使用的时候就非常不美观。
Inlined function with reified to the rescue
另一方面,如果你使用了 reified
泛型的T
在 inline
函数,这样你的T
的值就能在运行时访问,并且你不需要额外的传入这个泛型Class<T>
作为参数。你可以使用T
就像一个普通的Class
,例如,你可能需要检查变量是否是T
类型的实例,这里你就可以简单的写成:myVar is T
。
一个reified
类型的inline
函数看起来就像这样:
inline fun <reified T> myGenericFun()
必须清楚的是,reified
类型只能和inline
函数组合使用,一个inline
函数会使编译器在编译阶段以字节码复制到每个使用这个函数的地方(这就是我们说的这个函数已经inlined
)。当调用到这个使用reified
类型的inline
函数的时候,编译器知道这个真实类型作为参数类型,并且直接使用对应的类生成字节码替换。这样调用myVar is T
的地方就变成了myVar is String
(如果这里参数的类型是String的话)
Reified in Action
让我们来看一个例子,来看看这个reified
是怎么用的。我们想给String
写一个叫toKotlinObject
的扩展,它用来转化json字符串成为一个kotlin对象,使用的是泛型T
。我们可以使用com.fasterxml.jackson.module.kotlin
实现,方法如下:
Compile Error
fun <T> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
//does not compile!
return mapper.readValue(JsonObject(this).encode(), T::class.java)
}
这里的readValue
方法需要我们提供是使用什么类型供它转化这个json对象,但是我们给的是T
这个泛型。所以它就不能通过编译,编译器会说:Cannot use ‘T’ as reified type parameter. Use a class instead.
Working example without reified
fun <T> String.toKotlinObject(c: Class<T>): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(JsonObject(this).encode(), c)
}
这次我们传入了这个泛型的类,这样readValue
它能通过传入的c
判断到它需要的类型。这就是我们平常在java里面使用的方式。这样调用的时候这样写:
"{}".toKotlinObject(MyJsonType::class.java)
With reified
现在我们使用reified
类型的inline
函数是怎么实现的:
inline fun <reified T> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(JsonObject(this).encode(), T::class.java)
}
这样就不再需要传入T的实例,而直接可以作为普通类进行。调用方式如下:
"{}".toKotlinObject<MyJsonType>()
Important
inline
reified
的函数不能在java代码中使用,但是普通的inline
函数是可以的。这可能就是为什么不是每个inline
函数的参数类型用reified
作为默认类型的原因
Conclusion
这是一个快速的reified
类型介绍。在我看来使用reified
类型在函数中使用是一个更好的方式,因为当有相关的泛型的时候我们可以用普通的<>语法来表示。最后,这样的方式写的代码可读性也比java那样传入Class
参数要好的多。所有详细信息可以读官方文档
如果你想知道更多kotlin的精彩的特点,我推荐你阅读这本《Kotlin in Action》的书,同时也欢迎你来看我写的博客文章
Keep coding!