我试图了解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如何工作
您只能将reifiedinline函数结合使用。这样,您指示编译器将函数的字节码复制到调用该函数的每个位置(编译器“内联”该函数)。当您使用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解析为的类型。如果我们尝试获取类型参数ClassT,则编译器会抱怨:“不能将'T'用作化类型参数。请使用类。”
b)使用显式Class参数的解决方法
fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, c.java)
}
解决方法是,可以将ClassT用作方法参数,然后将其用作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)
}
无需再加上ClassT,就可以将T当作普通类使用。对于客户端,代码如下所示:
json.toKotlinObject<MyJsonType>()
重要说明:使用Java
不能从Java 代码调用类型的内联函数reified

09-11 20:55