1. Introduction

Type erasure is a technique used by the compiler to remove or erase the type information of a generic class at runtime in Java. That is, it enforces type constraints only at compile time while discarding the element type information at runtime, thereby ensuring type safety at runtime.

However, Kotlin uses generics to write abstract and reusable code that can work with different types of data. At compile time, the type of the generic parameter is known, which enables the compiler to perform type-checking and ensure that the code is type-safe.

There are times when we have to deal with lists of unknown types, and in those situations, we need to seek techniques that will help us to convert such lists to arrays of a particular type. In this tutorial, we’ll explore various ways to achieve this in Kotlin.

2. Using the toTypedArray() Method

One way to convert a List of unknown types to an Array in Kotlin is by using the built-in toTypedArray() method. Because the type is unknown, the only safe assumption is to convert to an array of type Any:

@Test
fun `convert type-erased list to array using toTypedArray`() {
    val intList: List<Any> = listOf(1, 2, 3, 4, 5)
    val stringList: List<Any> = listOf("one", "two", "three", "four", "five")

    val array1 = intList.toTypedArray()
    val array2 = stringList.toTypedArray()
        
    assertArrayEquals(arrayOf(1, 2, 3, 4, 5), array1)
    assertArrayEquals(arrayOf("one", "two", "three", "four", "five"), array2)
}

In this code snippet, the variables array1 and array2 are Arrays of type Any. If we’d attempted to cast them to an array of a specific type, we would’ve gotten a ClassCastException.

2.1. Using Inline Functions and Reified Type Parameters

Now that we’ve seen the example of converting a generic array, let’s look at how to strongly type the array. So, to convert the list of unknown types into an array of a specific type, we can use reified type parameters with an inline function:

inline fun <reified T> mapToArray(list: List<*>): Array<T> {
    return list.mapNotNull{ it as? T }.toTypedArray()
}

We define an inline function mapToArray() that accepts a list of unknown elements with a reified type parameter T. Note that the reified keyword allows us to access the type information of T at runtime.

This method uses the mapNotNull() method to obtain a list containing only non-null results after applying the transformation function. The transform function safely casts each list element to our reified type T. Note that during casting, any list element that is not of the correct type will be filtered out from the resulting list after the mapNotNull() method. Finally, we use the toTypedArray() method to convert the list to an array of type T.

Therefore, we obtain an array with all the elements from the input list, but with a newly-casted specific type.

Now, let’s be sure our method works correctly:

@Test
fun `convert type-erased list to array using toTypedArray with reified and inline`() {
    val intList: List<Any> = listOf(1, 2, 3, 4, 5)
    val stringList: List<Any> = listOf("one", "two", "three", "four", "five")

    val array1 = mapToArray<Int>(intList)
    val array2 = mapToArray<String>(stringList)

    assertArrayEquals(arrayOf(1, 2, 3, 4, 5), array1)
    assertArrayEquals(arrayOf("one", "two", "three", "four", "five"), array2)
}

3. Using the Array() Constructor

Another way to achieve our goal is by using the Array() constructor. It accepts two parameters — the size of the array and a lambda function that initializes each element:

@Test
fun `convert type-erased list to array using array constructor`() {
    val intList: List<Any> = listOf(1, 2, 3, 4, 5)
    val stringList: List<Any> = listOf("one", "two", "three", "four", "five")

    val array1 = Array(intList.size) { i -> intList[i] as Int}
    val array2 = Array(stringList.size) { i -> stringList[i] as String}

    assertArrayEquals(arrayOf(1, 2, 3, 4, 5), array1)
    assertArrayEquals(arrayOf("one", "two", "three", "four", "five"), array2)
}

In the code above, we convert two lists of unknown type elements into an array using the Array() constructor. Furthermore, we cast each element in the initialization lambda of the constructor to a specific type.

4. Conclusion

In this article, we’ve learned the difference between type-erasure and generics of unknown types. We also explored various ways we can convert a list of unknown element types to an array of a specific type in Kotlin.

The first approach demonstrates the use of the toTypedArray() method and how it comes short in actually converting a list of unknown element types to an array of specific types. However, we used inline functions and reified types to allow us to cast the result to a specific type instead.

Furthermore, all these approaches rely on Kotlin built-in methods like toTypedArray() and Array constructor methods. Each of these methods can be used according to our project’s needs.

As always, the code used in this article is available over on GitHub.