Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
806 views
in Technique[技术] by (71.8m points)

generics - How does the reified keyword in Kotlin work?

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?

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

TL;DR: What is reified good for

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

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.

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.

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


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:

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

c) 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...