Reputation: 9584
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
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