Salomon BRYS
Salomon BRYS

Reputation: 9584

Kotlin's reflection : Unknown type parameter

I am running some experiments on Kotlin's reflection.

I am trying to get a reflection object of a generic class with its argument.

In Java, that would be a ParameterizedType.

The way to get such a thing using Java's reflection API is a bit convoluted: create an anonymous subclass of a generic class, then get its super-type first parameter.

Here's an example:

@Suppress("unused") @PublishedApi
internal abstract class TypeReference<T> {}

inline fun <reified T> jGeneric() =
    ((object : TypeReference<T>() {}).javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0]

When I println(jGeneric<List<String?>>()), it prints java.util.List<? extends java.lang.String>, which is logical considering that Kotlin's List uses declaration-site out variance and that Java types have no notion of nullability.

Now, I would like to achieve the same kind of result, but with the Kotlin reflection API (that would, of course, contain nullability information).

Of course, List<String>::class cannot work since it yields a KClass. and I am looking for a KType.

However, when I try this:

inline fun <reified T> kGeneric() =
    (object : TypeReference<T>() {})::class.supertypes[0].arguments[0].type

When I println(kGeneric<List<String?>>()), it prints [ERROR : Unknown type parameter 0], which is quite... well, anticlimactic ;)

How can I get, in Kotlin, a KType reflecting List<String> ?

Upvotes: 4

Views: 4098

Answers (1)

Alexander Udalov
Alexander Udalov

Reputation: 32776

To create a KType instance in Kotlin 1.1, you have two options:

  • To create a simple non-nullable type out of a KClass, where the class is either not generic or you can substitute all its type parameters with star projections (*), use the starProjectedType property. For example, the following creates a KType representing a non-nullable type String:

    val nonNullStringType = String::class.starProjectedType
    

    Or, the following creates a KType representing a non-nullable type List<*>:

    val nonNullListOfSmth = List::class.starProjectedType
    
  • For more complex cases, use the createType function. It takes the class, type arguments and whether or not the type should be nullable. Type arguments are a list of KTypeProjection which is simply a type + variance (in/out/none). For example, the following code creates a KType instance representing List<String>:

    val nonNullStringType = String::class.starProjectedType
    val projection = KTypeProjection.invariant(nonNullStringType)
    val listOfStrings = listClass.createType(listOf(projection))
    

    Or, the following creates the type List<String>?:

    val listOfStrings = listClass.createType(listOf(projection), nullable = true)
    

Both starProjectedType and createType are defined in package kotlin.reflect.full.

We're planning to introduce the possibility of getting a KType instance simply from a reified type parameter of an inline function which would help in some cases where the needed type is known statically, however currently it's not entirely clear if that's possible without major overhead. So, until that's implemented, please use the declarations explained above.

Upvotes: 12

Related Questions