我试图了解reified
关键字(显然是it's allowing us to do reflection on generics)的用途。
但是,当我忽略它时,它也可以正常工作。有人在乎要解释何时使与有所不同吗?
最佳答案
TL; DR:reified
有什么用处
fun <T> myGenericFun(c: Class<T>)
在像myGenericFun
这样的通用函数的主体中,您无法访问T
类型,因为它的仅在编译时可用,而在运行时为erased。因此,如果要将泛型用作函数体中的普通类,则需要显式传递该类作为参数,如myGenericFun
所示。如果您使用化的
inline
创建T
函数,则即使在运行时也可以访问T
的类型,因此您无需额外传递Class<T>
。您可以像对待普通类一样使用T
-例如您可能想检查变量是否是T
的实例,可以轻松地做到这一点:myVar is T
。具有
inline
类型reified
的此类T
函数如下所示:inline fun <reified T> myGenericFun()
reified
如何工作您只能将
reified
与inline
函数结合使用。这样,您指示编译器将函数的字节码复制到调用该函数的每个位置(编译器“内联”该函数)。当您使用inline
类型调用reified
函数时,编译器必须能够知道作为类型参数传递的实际类型,以便编译器可以修改生成的字节码以直接使用相应的类。因此,像myVar is T
这样的调用将在字节码中变为myVar is String
(如果type参数为String
)。例
让我们看一个显示
reified
有多么有用的示例。我们想为
String
创建一个扩展函数toKotlinObject
,该扩展函数尝试将JSON字符串转换为具有该函数的通用类型T
指定的类型的普通Kotlin对象。我们可以为此使用 com.fasterxml.jackson.module.kotlin
,第一种方法是:a)不使用类型化的第一种方法
fun <T> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
//does not compile!
return mapper.readValue(this, T::class.java)
}
readValue
方法采用一种应将JsonObject
解析为的类型。如果我们尝试获取类型参数Class
的T
,则编译器会抱怨:“不能将'T'用作化类型参数。请使用类。” b)使用显式
Class
参数的解决方法fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, c.java)
}
解决方法是,可以将Class
的T
用作方法参数,然后将其用作readValue
的参数。这是可行的,并且是通用Java代码中的常见模式。可以这样称呼:data class MyJsonType(val name: String)
val json = """{"name":"example"}"""
json.toKotlinObject(MyJsonType::class)
c)Kotlin方式:reified
通过将
inline
函数与reified
类型参数T
结合使用,可以实现不同的功能:inline fun <reified T: Any> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, T::class.java)
}
无需再加上Class
的T
,就可以将T
当作普通类使用。对于客户端,代码如下所示:json.toKotlinObject<MyJsonType>()
重要说明:使用Java不能从Java 代码调用类型的内联函数
reified
。