I have an interface cont...">

The Kotlin Array <T> generator results in "Cannot use T as a parameter of type reified. Use a class instead," but List <T>

I have an interface containing an array (or list) of T and some metadata.

interface DataWithMetadata<T> { val someMetadata: Int fun getData(): Array<T> } 

If I write the simplest implementation of the interface, I get a compilation error in emptyArray() : "It is impossible to use T as a parameter of type reified. Instead, use a class."

 class ArrayWithMetadata<T>(override val someMetadata: Int): DataWithMetadata<T> { private var myData: Array<T> = emptyArray() override fun getData(): Array<T> { return myData } fun addData(moreData: Array<T>) { this.myData += moreData } } 

However, if I change both the interface and the implementation to a list, I have no problem compiling:

 interface DataWithMetadata<T> { val someMetadata: Int fun getData(): List<T> } class ListWithMetadata<T>(override val someMetadata: Int): DataWithMetadata<T> { private var myData: List<T> = emptyList() override fun getData(): List<T> { return myData } fun addData(moreData: Array<T>) { this.myData += moreData } } 

I suspect there is an interesting lesson in Kotlin's generics in my problem. Can someone tell me what the compiler does under the hood, and why the array is not working, but List is not working? Is there an idiomatic way to make an array implementation compile in this context?

Bonus question: The only reason I came to Array over List is because I often see Kotlin developers prefer arrays. Is that so, and if so, why?

+7
java arrays generics reification kotlin
source share
2 answers

Considering the declaration of emptyArray() in kotlin stdlib (jvm), we notice a parameter of type reified :

 public inline fun <reified @PureReifiable T> emptyArray(): Array<T> 

The reified parameter means that at compile time you have access to the T class and can access it like T::class . You can learn more about parameters of type reified in the Kotlin link . Since Array<T> compiles in java T[] , we need to know the type at compile time, hence the reified parameter. If you try to write the emptyArray () function without the reified , you will get a compiler error:

 fun <T> emptyArray() : Array<T> = Array(0, { throw Exception() }) 

Cannot use T as a parameter of type reified. Use a class instead.


Now consider the implementation of emptyList() :

 public fun <T> emptyList(): List<T> = EmptyList 

This implementation does not need the parameter T at all. It simply returns the internal EmptyList object, which itself inherits from List<Nothing> . The kotlin Nothing type is the return type of the throw keyword and is a value that never exists ( reference ). If the method returns Nothing , it is equivalent to throwing an exception in this place. Therefore, we can safely use Nothing here, because every time we call EmptyList.get() , the compiler knows that this will throw an exception.


Bonus question:

Starting with Java and C ++, I'm used to ArrayList or std::vector much easier to use these arrays. I have been using kotlin for several months now, and I usually don’t see much difference between arrays and lists when writing the source code. Both have tons of useful extension functions that behave in a similar way. However, the Kotlin compiler handles arrays and lists very different, since Java compatibility is very important for the Kotlin team. I usually prefer to use lists, and what I would recommend in your case too.

+5
source share

The problem is that the type of the generic Array element must be known at compile time, which is indicated here by a parameter of type reified , as shown in the declaration:

 public inline fun <reified @PureReifiable T> emptyArray(): Array<T> 

You can only create specific arrays of type Array<String> or Array<Int> , but not of type Array<T> .

You can find some workarounds in this answer . Hope you find a suitable way.

+2
source share

All Articles