Kotlin中的关键字关键字如何工作? [英] How does the reified keyword in Kotlin work?

查看:175
本文介绍了Kotlin中的关键字关键字如何工作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解 reified 关键字的用途,显然它允许我们对泛型进行反思。然而,当我放弃它时,它的工作原理也一样好。任何人都会在解释何时实现差异



在一个普通的泛型函数中,比如
<$ 通用的好的 p $ p> fun< T> myGenericFun(c:Class< T>)

不能访问类型 T ,因为它仅在编译时可用,但 擦除 。因此,如果要将泛型类型用作函数体中的普通类,您需要显式地将该类作为参数传递,如 myGenericFun



如果使用通用 <$ c创建 inline 函数但是,即使在运行时也可以访问 T 的类型,因此您不需要传递 > Class< T> 另外。您可以使用 T ,就好像它是普通的课程,例如你可能想检查一个变量是否是一个 T 的实例,你可以很容易地做到这一点: myVar是T



c 这个 inline code $> $ T $ / code> < reified T> myGenericFun()






通过工作



您只能使用 reified inline 功能。这样的函数使得编译器将函数的字节码复制到函数正在被使用的每个地方(函数被内联)。
当您使用具体化类型调用内联函数时,编译器知道用作类型参数的实际类型,并将生成的字节码修改为直接使用相应的类。
因此,像 myVar这样的调用是T 变为 myVar是String (如果类型参数是字符串)。






示例



让我们来看看一个例子,它显示了 reified 的用处。
我们希望为 String 创建一个称为 toKotlinObject 的扩展函数,它试图将JSON字符串转换为普通Kotlin对象,其类型由函数的泛型类型 T 指定。我们可以使用 com.fasterxml.jackson.module.kotlin

不使用具体类型的第一种方法

  fun< T> String.toKotlinObject():T {
val mapper = jacksonObjectMapper()
//不能编译!
return mapper.readValue(this,T :: class.java)
}

readValue 方法采用一种类型,它应该将 JsonObject 解析为。如果我们尝试获取类型参数 T Class ,编译器会抱怨:无法使用' T作为实体类型参数,使用类来代替。



解决方法使用显式 Class code>参数

  fun< T:Any> String.toKotlinObject(c:KClass< T>):T {
val mapper = jacksonObjectMapper()
返回mapper.readValue(this,c.java)
}

作为一种解决方法, T的 Class 可以作为方法参数,然后用作 readValue 的参数。这是可行的,并且是通用Java代码中的常见模式。它可以被调用如下:

  data class MyJsonType(val name:String)

val json ={name:example}
json.toKotlinObject(MyJsonType :: class)

Kotlin方式:通货膨胀



使用内联函数并使用指定类型参数 T 允许以不同的方式实现该功能:

 内联fun  val mapper = jacksonObjectMapper()
返回mapper.readValue(this,T :: class.java)
}

不需要使用 T 的 Class code>另外, T 可以像使用普通类一样使用。对于客户端,代码如下所示:

  json.toKotlinObject< MyJsonType>()


使用 reified 类型不能从Java 代码中调用。


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: What is reified good for

In an ordinary generic function like

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

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.

If you create an inline function with a reified T though, 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.

Such an inline function with reified type T looks as follows:

inline fun <reified T> myGenericFun()


How reified works

You can only use reified in combination with an inline function. Such a function makes the compiler copy the function's bytecode to every place where the function is being used (the function is being "inlined"). When you call an inline function with reified type, the compiler knows the actual type used as a type argument and modifies the generated bytecode to use the corresponding class directly. Therefore calls like myVar is T become myVar is String (if the type argument were String) in the bytecode and at runtime.


Example

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:

First approach without reified type

fun <T> String.toKotlinObject(): T {
      val mapper = jacksonObjectMapper()
                                                        //does not compile!
      return mapper.readValue(this, T::class.java)
}

The readValue method takes a type that it’s 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."

Workaround with explicit Class parameter

fun <T: Any> String.toKotlinObject(c: KClass<T>): T {
    val mapper = jacksonObjectMapper()
    return mapper.readValue(this, c.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)

The Kotlin way: reified

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)
}

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>()

Important Note: Working with Java

An inlined function with reified type is not callable from Java code.

这篇关于Kotlin中的关键字关键字如何工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆