I saw many similar topics, but did not find suitable for me.
I have a parent class sealed class Item
and a few nested:
data class A(val a: Int) : Item()
data class B(val b: Int) : Item()
There are a lists:
val aList = listOf<A>(...)
val bList = listOf<B>(...)
and some method which take a list as argument:
fun processList(list: List<Item>) {...}
How to get specific type in the processList
method?
I know about the way with reified
type using:
inline fun <reified T: Item> processList(list: List<T>) {
val type = T::class
...
}
but it cannot work with vararg
arguments:
fun processList(vararg items: List<Item>) {...}
which is called for example processList(aList, bList)
.
Is it possible to get type of list elements without generics? The only idea is get type of first list element, but it is not a very nice solution.
CodePudding user response:
Disclaimer: author of this question explained in comments that they need a way to receive vararg
of lists and then get the parameter type of each list separately.
General answer is that this is not possible. Due to type erasure in Java/Kotlin, generic types are not known at runtime. reified
introduced by Kotlin is an attempt to mitigate this problem at least partially, although it has its limitations as well. There are some ways to accomplish this, but they're all more like workarounds than real solutions:
1. Do not use vararg
, but overload the function.
inline fun <reified T1: Item> processList(list1: List<T1>) {}
inline fun <reified T1: Item, T2: Item> processList(list1: List<T1>, list2: List<T2>) {}
inline fun <reified T1: Item, T2: Item, T3: Item> processList(list1: List<T1>, list2: List<T2>, list3: List<T3>) {}
...
Sounds really bad, but it takes 10 minutes to write such functions for up to 10 params and you do this once. Many libraries do something similar.
You can delegate each above function to a single one that accepts a list/array of KClass
/Class
, so your real implementation will be in a single place.
2. Check the first item of each list.
Very hacky and it has its limitations. You don't get the compile type, but the runtime type, which depending on your case may be not at all acceptable. It won't work well with heterogeneous collections. Also, if you need to know the type of empty lists, then this is not possible.
3. Create your own implementation of a list which keeps its type.
fun main() {
val aList = myListOf<A>()
val bList = myListOf<B>()
processList(aList, bList)
}
// or:
fun main() {
val aList = listOf<A>()
val bList = listOf<B>()
processList(aList.asMyList(), bList.asMyList())
}
fun processList(vararg items: MyList<Item>) {
println(items[0].type)
println(items[1].type)
}
inline fun <reified T : Any> myListOf(vararg items: T): MyList<T> = MyList(T::class, listOf(*items))
inline fun <reified T : Any> List<T>.asMyList(): MyList<T> = MyList(T::class, this)
class MyList<out T : Any> @PublishedApi internal constructor(
val type: KClass<out T>,
private val delegate: List<T>
) : List<T> by delegate
Of course, it has the obvious limitation that we have to use our specific list implementation.