Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin generics Array<T> results in "Cannot use T as a reified type parameter. Use a class instead" but List<T> does not

I have an interface that contains 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 compile error on the emptyArray(): "Cannot use T as a reified type parameter. Use a class instead."

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 compile-time issues:

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 some interesting lesson in Kotlin generics inside my issue. Can anyone tell me what the compiler is doing under the hood and why Array fails but List does not? Is there an idiomatic way to make the Array implementation compile in this context?

Bonus question: The only reason I reached for Array over List is that I often see Kotlin developers favor Arrays. Is this the case, and if so, why?

like image 466
Nate Vaughan Avatar asked Oct 22 '17 00:10

Nate Vaughan


People also ask

Can use T as reified type parameter?

Cannot use 'T' as reified type parameter. Use a class instead. The possible way to access the type of the Template class is by passing it as a parameter. Since we can not use the type of template at runtime directly, we need to use reified here.

What is reified in Kotlin?

"reified" is a special type of keyword that helps Kotlin developers to access the information related to a class at runtime. "reified" can only be used with inline functions. When "reified" keyword is used, the compiler copies the function's bytecode to every section of the code where the function has been called.

How do I use generic Kotlin?

If a generic type has several type parameters, each of them can be projected independently. For example, if the type is declared as interface Function<in T, out U> you could use the following star-projections: Function<*, String> means Function<in Nothing, String> . Function<Int, *> means Function<Int, out Any?> .


1 Answers

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

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

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

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

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


Now, let's take a look at the implementation of emptyList():

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

This implementation doesn't need the parameter T at all. It just returns the internal object EmptyList, which itself inherits from List<Nothing>. The kotlin type Nothing is the return-type of the throw keyword and is a value that never exists (reference). If a method returns Nothing, is is equivalent to throwing an exception at that place. So we can safely use Nothing here because everytime we would call EmptyList.get() the compiler knows that this will return an exception.


Bonus question:

Coming from Java and C++, I am used to ArrayList or std::vector to be far easier to use that arrays. I use kotlin now for a few month and I usually don't see a big difference between arrays and lists when writing source code. Both have tons of usefull extension functions which behave in a similar way. However, the Kotlin compiler handles arrays and lists very different, as Java interoperability is very important for the Kotlin team. I usually prefer using lists, and that's what I'd recommend in your case too.

like image 189
msrd0 Avatar answered Sep 22 '22 16:09

msrd0