Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get actual type arguments of a reified generic parameter in Kotlin?

Using reified type parameters, one can write an inline function which works with the type parameter through reflection at runtime:

inline fun <reified T: Any> f() {     val clazz = T::class     // ... } 

But when f is called with a parameter which is itself a generic class, there seems to be no way to obtain its actual type arguments through T::class:

f<List<Integer>>() // T::class is just kotlin.collections.List 

Is there a way to get actual type arguments of a reified generic through reflection?

like image 691
hotkey Avatar asked Mar 27 '16 22:03

hotkey


People also ask

How do you get the class of generic type in Kotlin?

There are no direct ways to do this in Kotlin. In order to check the generic type, we need to create an instance of the generic class<T> and then we can compare the same with our class.

Which types can be used as arguments of a generic type?

The actual type arguments of a generic type are. reference types, wildcards, or. parameterized types (i.e. instantiations of other generic types).

What is reified type in Kotlin?

Reified keyword in KotlinIf a function is marked as inline , then wherever the function is called, the compiler will paste the whole body of the function there.

How do I get a class instance of generic type T?

The short answer is, that there is no way to find out the runtime type of generic type parameters in Java. A solution to this is to pass the Class of the type parameter into the constructor of the generic type, e.g.


1 Answers

Due to type erasure, actual generic arguments cannot be obtained through T::class token of a generic class. Different objects of a class must have the same class token, that's why it cannot contain actual generic arguments.

But there is a techinque called super type tokens which can give actual type arguments in case when the type is known at compile time (it is true for reified generics in Kotlin because of inlining).


Edit: Since Kotlin 1.3.50, following the technique described below to get type information for a reified type parameter is no longer necessary. Instead, you can use typeOf<T>() on reified type parameters.


The trick is that the compiler retains actual type arguments for a non-generic class derived from a generic class (all its instances will have the same arguments, good explanation here). They are accessible through clazz.genericSuperClass.actualTypeArguments of a Class<*> instance.

Given all that, you can write a util class like this:

abstract class TypeReference<T> : Comparable<TypeReference<T>> {     val type: Type =          (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]      override fun compareTo(other: TypeReference<T>) = 0 } 

Explained in Jackson TypeReference which uses the same approach. Jackson Kotlin module uses it on reified generics.

After that, in an inline function with a reified generic, TypeReference needs to be subclassed (an object expression will go), and then its type can be used.

Example:

inline fun <reified T: Any> printGenerics() {     val type = object : TypeReference<T>() {}.type     if (type is ParameterizedType)         type.actualTypeArguments.forEach { println(it.typeName) } } 

printGenerics<HashMap<Int, List<String>>>():

java.lang.Integer java.util.List<? extends java.lang.String> 
like image 200
hotkey Avatar answered Oct 04 '22 18:10

hotkey