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?
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?
reified
good forfun <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
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()
reified
worksYou 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.
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’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."
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>()
An inlined function with reified
type is not callable from Java code.