9

I need to be able to tell the generic type of kotlin collection at runtime. How can I do it?

val list1 = listOf("my", "list")
val list2 = listOf(1, 2, 3)
val list3 = listOf<Double>()

/* ... */

when(list.genericType()) {
    is String -> handleString(list)
    is Int -> handleInt(list)
    is Double -> handleDouble(list)
}
pawelo
  • 1,367
  • 3
  • 14
  • 28
  • Are you targeting JVM or js? On JVM there are some reflection magic besides the kotlin refied types, but on js backend all you can rely on is refied type. – glee8e Apr 03 '17 at 15:59

2 Answers2

12

Kotlin generics share Java's characteristic of being erased at compile time, so, at run time, those lists no longer carry the necessary information to do what you're asking. The exception to this is if you write an inline function, using reified types. For example this would work:

inline fun <reified T> handleList(l: List<T>) {
    when (T::class) {
        Int::class -> handleInt(l)
        Double::class -> handleDouble(l)
        String::class -> handleString(l)
    }
}

fun main() {
    handleList(mutableListOf(1,2,3))
}

Inline functions get expanded at every call site, though, and mess with your stack traces, so you should use them sparingly.

Depending on what you're trying to achieve, though, there's some alternatives. You can achieve something similar at the element level with sealed classes:

sealed class ElementType {
    class DoubleElement(val x: Double) : ElementType()
    class StringElement(val s: String) : ElementType()
    class IntElement(val i: Int) : ElementType()
}

fun handleList(l: List<ElementType>) {
    l.forEach {
        when (it) {
            is ElementType.DoubleElement -> handleDouble(it.x)
            is ElementType.StringElement -> handleString(it.s)
            is ElementType.IntElement -> handleInt(it.i)
        }
    }
}
pdpi
  • 3,467
  • 2
  • 18
  • 29
8

You can use inline functions with reified type parameters to do that:

inline fun <reified T : Any> classOfList(list: List<T>) = T::class

(runnable demo, including how to check the type in a when statement)

This solution is limited to the cases where the actual type argument for T is known at compile time, because inline functions are transformed at compile time, and the compiler substitutes their reified type parameters with the real type at each call site.

On JVM, the type arguments of generic classes are erased at runtime, and there is basically no way to retrieve them from an arbitrary List<T> (e.g. a list passed into a non-inline function as List<T> -- T is not known at compile-time for each call and is erased at runtime)

If you need more control over the reified type parameter inside the function, you might find this Q&A useful.

Community
  • 1
  • 1
hotkey
  • 111,884
  • 27
  • 298
  • 285