Kotlin 中的 reified 关键字是如何工作的? [英] How does the reified keyword in Kotlin work?
问题描述
我试图了解 reified
关键字的用途,显然是 它允许我们对泛型进行反思.
I'm trying to understand the purpose of the reified
keyword, apparently it's allowing us to do reflection on generics.
但是,当我不使用它时,它也能正常工作.有人愿意解释这什么时候会产生实际差异吗?
However, when I leave it out it works just as fine. Anyone care to explain when this makes an actual difference?
推荐答案
TL;DR:reified
有什么用
fun <T> myGenericFun(c: Class<T>)
在像 myGenericFun
这样的泛型函数的主体中,您不能访问类型 T
因为它仅在编译时可用但是在运行时擦除.因此,如果您想在函数体中使用泛型类型作为普通类,您需要显式地将类作为参数传递,如myGenericFun
所示.
In the body of a generic function like myGenericFun
, you can't access the type T
because it's only available at compile time but erased at runtime. Therefore, if you want to use the generic type as a normal class in the function body you need to explicitly pass the class as a parameter as shown in myGenericFun
.
如果你创建一个带有reified T
的inline
函数,T
的类型甚至可以被访问在运行时,因此您不需要额外传递 Class
.您可以像使用普通类一样使用 T
- 例如你可能想检查一个变量是否是一个实例 T
,你可以很容易地做到:myVar is T
.
If you create an inline
function with a reified T
, the type of T
can be accessed even at runtime, and thus you do not need to pass the Class<T>
additionally. You can work with T
as if it was a normal class - e.g. you might want to check whether a variable is an instance of T
, which you can easily do then: myVar is T
.
这样一个reified
类型T
的inline
函数如下所示:
Such an inline
function with reified
type T
looks as follows:
inline fun <reified T> myGenericFun()
reified
如何工作
您只能将 reified
与 inline
函数结合使用.通过这样做,您指示编译器将函数的字节码复制到调用该函数的每个位置(编译器内联"该函数).当您使用 reified
类型调用 inline
函数时,编译器必须能够知道作为类型参数传递的实际类型,以便它可以修改生成的字节码以使用直接对应的类.因此,像 myVar is T
这样的调用在字节码中变成了 myVar is String
(如果类型参数是 String
).
How reified
works
You can only use reified
in combination with an inline
function. By doing so, you instruct the compiler to copy the function's bytecode to every spot the function is invoked from (the compiler "inlines" the function). When you call an inline
function with reified
type, the compiler has to be able to know the actual type passed as a type argument so that it can modify the generated bytecode to use the corresponding class directly. Therefore a call like myVar is T
becomes myVar is String
in the bytecode (if the type argument is String
).
让我们看一个例子,它展示了 reified
是多么有用.我们想为 String
创建一个名为 toKotlinObject
的扩展函数,它尝试将 JSON 字符串转换为普通 Kotlin 对象,其类型由函数的泛型类型 T 指定代码>.我们可以使用
com.fasterxml.jackson.module.kotlin
为此,第一种方法如下:
Let's have a look at an example that shows how helpful reified
can be.
We want to create an extension function for String
called toKotlinObject
that tries to convert a JSON string to a plain Kotlin object with a type specified by the function's generic type T
. We can use com.fasterxml.jackson.module.kotlin
for this and the first approach is the following:
a) 第一种没有具体化类型的方法
fun <T> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
//does not compile!
return mapper.readValue(this, T::class.java)
}
readValue
方法采用一种应该将 JsonObject
解析为的类型.如果我们尝试获取类型参数 T
的 Class
,编译器会抱怨:"Cannot use 'T' as reified type parameter.改用类."
The readValue
method takes a type that it is supposed to parse the JsonObject
to. If we try to get the Class
of the type parameter T
, the compiler complains: "Cannot use 'T' as reified type parameter. Use a class instead."
b) 使用显式 Class
参数
b) Workaround with explicit Class
parameter
fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, c.java)
}
作为一种解决方法,可以将 T
的 Class
设为方法参数,然后将其用作 readValue
的参数.这是有效的,并且是通用 Java 代码中的常见模式.可以这样调用:
As a workaround, the Class
of T
can be made a method parameter, which then used as an argument to readValue
. This works and is a common pattern in generic Java code. It can be called as follows:
data class MyJsonType(val name: String)
val json = """{"name":"example"}"""
json.toKotlinObject(MyJsonType::class)
c) Kotlin 方式:reified
使用带有 reified
类型参数 T
的 inline
函数可以实现不同的函数:
Using an inline
function with reified
type parameter T
makes it possible to implement the function differently:
inline fun <reified T: Any> String.toKotlinObject(): T {
val mapper = jacksonObjectMapper()
return mapper.readValue(this, T::class.java)
}
T
的Class
不需要另外取,T
可以像普通类一样使用.对于客户端,代码如下所示:
There’s no need to take the Class
of T
additionally, T
can be used as if it was an ordinary class. For the client the code looks like this:
json.toKotlinObject<MyJsonType>()
重要说明:使用 Java
reified
类型的内联函数不能从 Java 代码中调用.
这篇关于Kotlin 中的 reified 关键字是如何工作的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!