Kotlin中的reified关键字如何工作?


144

我试图了解reified关键字的目的,显然它使我们能够对泛型进行反思

但是,当我忽略它时,它也可以正常工作。有人在乎解释时,这使得实际的区别


您确定知道反射是什么吗?另外,您是否听说过类型擦除?
米巴克'17

3
泛型类型参数在运行时被擦除,如果尚未阅读类型擦除,请阅读。内联函数具体化类型参数不仅内嵌的方法体,同时也是泛型类型参数,允许你做像T :: class.java事情(你不能正常泛型类型做)。把作为一个评论,因为我没有时间,现在要充实一个完整的答案..
F.乔治·

它允许访问函数的具体泛型,而无需依赖反射,也不必将类型作为参数传递。
BladeCoder

Answers:


368

TL; DR:有什么reified好处

fun <T> myGenericFun(c: Class<T>) 

在这类通用函数的主体中myGenericFun,您无法访问该类型,T因为它仅在编译时可用,而在运行时被擦除。因此,如果要将泛型类型用作函数体中的普通类,则需要显式传递该类作为参数,如中所示myGenericFun

但是,如果inline使用带修饰符 的函数创建函数T,则T即使在运行时也可以访问的类型,因此您无需Class<T>额外传递。您可以T像对待普通类一样使用它,例如,您可能想检查变量是否是的实例 T,可以轻松地进行操作:myVar is T

inline具有reified类型的此类函数T如下所示:

inline fun <reified T> myGenericFun()

如何reified工作

您只能reifiedinline功能结合使用。这种函数使编译器将函数的字节码复制到使用该函数的每个位置(该函数被“内联”)。当您调用带有修正类型的内联函数时,编译器知道用作类型实参的实际类型,并修改生成的字节码以直接使用相应的类。因此,在字节码中和在运行时,调用myVar is T变为成为myVar is String(如果type参数为String)。


让我们看一个示例,该示例显示了如何帮助reified您。我们要创建一个扩展功能StringtoKotlinObject,试图JSON字符串转换为纯科特林对象与函数的泛型类型指定的类型T。我们可以使用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为的类型。如果我们尝试获取Classtype参数的T,则编译器会抱怨:“不能将'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)科特林方式: reified

使用inline带有reified类型参数T的函数可以以不同的方式实现该函数:

inline fun <reified T: Any> String.toKotlinObject(): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, T::class.java)
}

无需另外使用Classof TT可以像普通类一样使用。对于客户端,代码如下所示:

json.toKotlinObject<MyJsonType>()

重要说明:使用Java

reified类型的内联函数不能从Java代码中调用


6
感谢您的全面回复!这实际上是有道理的。我想知道的一件事是,如果无论如何都内联函数,为什么需要整形?它会留下类型擦除并内联函数吗?对我来说这似乎是一种浪费,如果您内联函数,也可能内联正在使用的类型,或者我在这里看到错误了吗?
hl3mukkel

6
感谢您的反馈,实际上我忘了提到可能会给您答案的内容:可以从Java调用普通的内联函数,而不能使用带有类型化参数的函数!我认为这是为什么不是自动使内联函数的每个类型参数都被调整的原因。
s1m0nw1

如果函数混合了参数化参数和非参数化参数,该怎么办?这使得它无论如何都不能从Java调用,为什么不自动对所有类型参数进行调整呢?为什么Kotlin需要明确为所有类型参数指定具体化?
Vairavan

1
如果堆栈中的高层调用者不需要json.toKotlinObject <MyJsonType>(),而是需要json.toKotlinObject <T>()用于不同的对象怎么办?
7

1

简单

*已修正是在编译时授予使用权限(在函数内部访问T)

例如:

 inline fun <reified T:Any>  String.convertToObject(): T{

    val gson = Gson()

    return gson.fromJson(this,T::class.java)

}

使用像:

val jsonStringResponse = "{"name":"bruno" , "age":"14" , "world":"mars"}"
val userObject = jsonStringResponse.convertToObject<User>()
  println(user.name)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.