mmorihiro
mmorihiro

Reputation: 877

How to convert a type-erased list to an array in Kotlin?

A function toArray should convert type-erased list to T that is Array<String> now.

inline fun <reified T> toArray(list: List<*>): T {
    return list.toTypedArray() as T
}

toArray<Array<String>>(listOf("a", "b", "c")) // should be arrayOf("a", "b", "c")

However, toArray throws this error.

java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;

Do you have any ideas?

Upvotes: 30

Views: 46940

Answers (3)

mfulton26
mfulton26

Reputation: 31274

To understand why this happens I recommend reading Difference between List and Array types in Kotlin and Java Practices -> Prefer Collections over older classes.

In general I recommend using lists instead of arrays but if you want to convert a type-erased list to an array in Kotlin the easiest way to do so is first mapping to a typed list and then converting to a typed array:

list.map { it as String }.toTypedArray()

If you don't actually need an array but a typed list will do then you can simply map the type-erased list to a typed list:

list.map { it as String }

If you find yourself often converting a type-erased list to an array and performance matters then I recommend creating a map function optimized for doing this while avoiding an intermediary list:

inline fun <T, reified R> List<T>.mapToTypedArray(transform: (T) -> R): Array<R> {
    return when (this) {
        is RandomAccess -> Array(size) { index -> transform(this[index]) }
        else -> with(iterator()) { Array(size) { transform(next()) } }
    }
}

You can then efficiently convert a List<*> to a typed array:

list.mapToTypedArray { it as String }

Upvotes: 14

epool
epool

Reputation: 6819

For some reason, I was getting an invalid argument number exception when I was trying to do this:

val argumentTypeMirrors = mutableListOf<TypeMirror>()
... // add items to argumentTypeMirrors
val array = argumentTypeMirrors.toTypedArray()

And I ended up doing it this way:

val argumentTypeMirrors = mutableListOf<TypeMirror>()
... // add items to argumentTypeMirrors
val array = Array(argumentTypeMirrors.size) {
    argumentTypeMirrors[it]
}

Then I was able to destruct my array with *array for passing it as a varargs parameter.

Upvotes: 1

Ruslan
Ruslan

Reputation: 14640

Problem here, is that you actually trying cast Object[] to String[] in terms of Java, or Array<Any> to Array<String> in terms of Kotlin, but this is different objects.

So, this statement:

list.toTypedArray()

returns Array<Any?>, and then you trying to cast it to Array<String> and get ClassCastException.

How to fix?

I suggest pass type parameter itself, and cast List:

inline fun <reified T> toArray(list: List<*>): Array<T> {
    return (list as List<T>).toTypedArray()
}

toArray<String>(listOf("1", "2"))

Upvotes: 61

Related Questions