Search code examples
kotlingenericskotlin-reified-type-parameters

Overload resolution of reified generic in Kotlin


I would like to return different type depending on a reified type parameter. I tried to use overloads but the overload resolution doesn't seem correct.

My goal is to store a close set of types at runtime like so:

sealed interface Type<T> {
  object Int: Type<kotlin.Int>
  object Boolean: Type<kotlin.Boolean>
}

inline fun<reified T> get() : Type<T> = getImpl(null as T?)

fun getImpl(a: Int?) : Type<Int> = Type.Int
fun getImpl(a: Boolean?) : Type<Boolean> = Type.Boolean
fun <T>getImpl(a: T?) : Type<T> = error("Unsupported type")

fun main() {
    println(getImpl(null as Int?)) // return Type.Int as expected
    println(get<Int>()) // Same as above after get is inlined but throws!
}

Could it be that the overload is resolved before the method is inlined?

The goal is for some generic classes to take a Type<T> parameter and be guaranty that T is in the closed set. It also allows for testing the generic type T at runtime (workaround type erasure).

I would rather avoid having the clients specify Type.Int explicitly or have an implementation using unchecked cast such as:

inline fun<reified T> getUncheckedCast() : Type<T> =
  when (T::class) {
    Int::class -> Type.IntType as Type<T>
    Boolean::class -> Type.BooleanType as Type<T>
    else -> error("Unsupported type")
  }

Solution

  • According to Kotlin documentation, the only difference reified parameter makes is that its runtime class is available:

    4.5.2 Reified type parametersLoad tests

    Type parameters of inline function declarations (and only those) can be declared reified using the corresponding keyword. A reified type parameter is a runtime-available type inside the function scope, see the corresponding section for details.

    It doesn't specify that the type is substituted when the function is inlined. This mean that reified is sugaring for implicitly passing the KClass of reified types:

    inline fun <reified T>f() = T.class
    // is desugared to
    inline fun <T>f(__TKClass : KClass<T>) = __TKClass
    

    Thus the overload resolution set is not affected by reifying types.