Parameterized and type-safe access to KTypeParameter


#1

I’d like access to the KTypeParameters of an interface or class for meta-programming, similar to the current access to class members:

interface ITyped<X: Number, Y>
val xParam: KTypeParameter<Number> = ITyped::X

Of course KTypeParameter or something similar would need to be generic in it’s bound.

Ideally, there would be the ability to resolve the value of a type parameter by a class that defines it, like TypeToken.resolve in guava:

class Concrete : ITyped<Int, String>

ITyped::X.resolveIn(Concerete::class) // => Int

I wasn’t able to find any other discussion about this sort of feature, why it’s apparently missing in Kotlin, and what the best way to do this sort of thing given the current functionality. Thanks.


#2

You can access the type parameters of a KClass using reflection: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-class/type-parameters.html

As for the second part, I don’t think it’s possible due to the jvm type erasure.


#3

I’m aware I can access the type parameters that way, but that’s specifically what I want to avoid. I have to access them by index, and they aren’t type-safe:

interface I<T> 
I::class.typeParameters[1] // This compiles. It shouldn't. There is only 1 type parameter.

Accessing them in a type-safe way would prevent this sort of error:

interface I<T : Number> 
I::typeParams::U // would not compile, because there is no type parameter U
I::typeParams::T // would compile, resulting in something like TypeParameter<I, Number>

Furthermore, it is absolutely possible to resolve the actual type of a parameter _when it is used as a superclass, super-interface, or implemented interface. See Guava’s TypeToken documention, and this article: https://www.javacodegeeks.com/2013/12/advanced-java-generics-retreiving-generic-type-arguments.html


#4

For now, I can use Guava’s TypeToken, but’s it not type-safe or elegant:

interface ITyped <X : Number, Y> {
    fun xType(): Class<Number> = TypeToken.of(this::class.java)
        .resolveType(Thing::class.java.typeParameters[0]) // Unsafe index
        .rawType as Class<Number> // Unsafe cast

    fun yType(): Class<Number> = TypeToken.of(this::class.java)
        .resolveType(Thing::class.java.typeParameters[1]) // Unsafe index
        .rawType as Class<Any> // Unsafe cast
}

Imagine if it could be like this:

interface ITyped <X : Number, Y> {
    fun xType() = ITyped::typeParams::X.resolveIn(this) // KClass<Number>
    fun yType() = ITyped::typeParams::Y.resolveIn(this) // KClass<Any>
}