63 votes

Tableau générique Kotlin <T> entraîne «Impossible d'utiliser T comme paramètre de type réifié. Utilisez une classe à la place» mais List <T> ne fait pas

J'ai une interface qui contient un tableau (ou une liste) de T et de certaines métadonnées.

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

Si j'écris le plus simple de mise en œuvre de l'interface, j'obtiens une erreur de compilation sur l' emptyArray(): "Impossible d'utiliser le T comme un réifiée paramètre de type. Utiliser une classe à la place."

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
    }
}

Cependant, si je modifie à la fois l'interface et la mise en œuvre d'une liste, je n'ai pas le temps de compilation des questions:

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
    }
}

Je soupçonne qu'il ya une intéressante leçon de Kotlin génériques à l'intérieur de mon problème. Quelqu'un peut-il me dire ce que le compilateur fait sous le capot et pourquoi Matrice échoue, mais la Liste n'est pas? Est-il un idiomatiques façon de faire de la Matrice de mise en œuvre de la compilation dans ce contexte?

Question Bonus: La seule raison pour laquelle j'ai atteint pour le Tableau sur la Liste, c'est que je vois souvent Kotlin développeurs faveur des Tableaux. Est-ce le cas, et si oui, pourquoi?

48voto

msrd0 Points 3996

En regardant la déclaration d' emptyArray() dans le kotlin stdlib (jvm), nous remarquons l' reified type de paramètre:

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

L' reified type de paramètre signifie que vous avez accès à la classe de T au moment de la compilation et pouvez y accéder comme T::class. Vous pouvez en lire plus à propos de reified type de paramètres dans le Kotlin de référence. Depuis Array<T> compilé en java T[], nous avons besoin de connaître le type à la compilation, d'où l' reified paramètre. Si vous essayez d'écrire un emptyArray() la fonction sans l' reified mot clé, vous obtiendrez une erreur de compilation:

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

Ne peut pas utiliser T comme un réifiée paramètre de type. Utiliser une classe à la place.


Maintenant, laissez-nous jeter un oeil à la mise en œuvre de l' emptyList():

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

Cette mise en œuvre n'a pas besoin de paramètre T à tous. Elle retourne l'objet interne EmptyList, qui elle-même hérite de List<Nothing>. Le kotlin type Nothing est le retour de l' throw mot-clé et une valeur qui n'existe jamais (de référence). Si une méthode retourne Nothing, est équivalent à la levée d'une exception à cet endroit. On peut donc l'utiliser en toute sécurité Nothing ici parce que chaque fois que nous appellerions EmptyList.get() le compilateur sait que cela va renvoyer une exception.


Question Bonus:

Venant de Java et de C++, je suis habitué ArrayList ou std::vector être beaucoup plus facile à utiliser que les tableaux. J'utilise kotlin maintenant quelques mois et j'ai l'habitude de ne pas voir une grande différence entre les tableaux et les listes lors de l'écriture du code source. Les deux ont des tonnes d'utiles fonctions d'extension qui se comportent de façon similaire. Cependant, la Kotlin compilateur gère des tableaux et des listes très différentes, comme Java interopérabilité est très important pour la Kotlin de l'équipe. D'habitude, je préfère utiliser les listes, et c'est ce que je recommande dans votre cas aussi.

16voto

s1m0nw1 Points 21698

Le problème est que le type générique de l' Array doit être connu au moment de la compilation, ce qui est indiqué par l' reified type de paramètre ici, comme on le voit dans la déclaration:

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

Il est seulement possible de créer béton tableaux comme Array<String> ou Array<Int> mais pas de type Array<T>.

Dans cette réponse, vous pouvez trouver plusieurs solutions de contournement. J'espère que vous trouverez une façon convenable.

1voto

Riki137 Points 477

La solution de contournement qui a fonctionné le mieux pour moi était:

 @Suppress("UNCHECKED_CAST")
var pool: Array<T?> = arrayOfNulls<Any?>(initialCapacity) as Array<T?>
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X