Tiré de Effective Java par Joshua Bloch,
- Les tableaux diffèrent du type générique de deux manières importantes. Les premiers tableaux sont covariants. Les génériques sont invariants.
Covariant signifie simplement que si X est un sous-type de Y, alors X [] sera également un sous-type de Y []. Les tableaux sont covariants Car string est le sous-type de Object So
String[] is subtype of Object[]
Invariant signifie simplement que X soit le sous-type de Y ou non,
List<X> will not be subType of List<Y>.
Ma question est pourquoi la décision de rendre les tableaux covariants en Java? Il existe d'autres articles SO tels que Pourquoi les tableaux sont invariants, mais les listes covariantes? , mais ils semblent se concentrer sur Scala et je ne suis pas en mesure de suivre.
java
arrays
generics
language-design
covariance
eagertoLearn
la source
la source
LinkedList
.Réponses:
Via wikipedia :
Cela répond à la question "Pourquoi les tableaux sont-ils covariants?", Ou plus précisément, "Pourquoi les tableaux ont- ils été rendus covariants à l'époque ?"
Lorsque les génériques ont été introduits, ils n'ont pas été délibérément rendus covariants pour les raisons exposées dans cette réponse de Jon Skeet :
La motivation originale pour rendre les tableaux covariants décrite dans l'article de wikipedia ne s'appliquait pas aux génériques car les jokers rendaient possible l'expression de la covariance (et de la contravariance), par exemple:
la source
Object[] num = new Number[4]; num[1]= 5; num[2] = 5.0f; num[3]=43.4; System.out.println(Arrays.toString(num)); num[0]="hello";
ArrayStoreException
s si nécessaire. À l’époque, cela était clairement considéré comme un bon compromis. Comparez cela avec aujourd'hui: beaucoup considèrent la covariance de tableau comme une erreur, rétrospectivement.La raison en est que chaque tableau connaît son type d'élément pendant l'exécution, contrairement à la collection générique à cause de l'effacement du type.
Par exemple:
Si cela était autorisé avec les collections génériques:
Mais cela poserait des problèmes plus tard lorsque quelqu'un essaierait d'accéder à la liste:
la source
Peut-être cette aide: -
Les génériques ne sont pas covariants
Les tableaux dans le langage Java sont covariants - ce qui signifie que si Integer étend Number (ce qu'il fait), non seulement un Integer est également un Number, mais un Integer [] est également un
Number[]
, et vous êtes libre de passer ou d'attribuer unInteger[]
où aNumber[]
est appelé. (Plus formellement, si Number est un supertype de Integer, alorsNumber[]
est un supertype ofInteger[]
.) Vous pourriez penser qu'il en va de même pour les types génériques - c'estList<Number>
un supertype deList<Integer>
, et que vous pouvez passer aList<Integer>
où aList<Number>
est attendu. Malheureusement, cela ne fonctionne pas de cette façon.Il s'avère qu'il y a une bonne raison pour laquelle cela ne fonctionne pas de cette façon: cela briserait le type de sécurité que les génériques étaient censés fournir. Imaginez que vous puissiez attribuer un
List<Integer>
à un fichierList<Number>
. Ensuite, le code suivant vous permettrait de mettre quelque chose qui n'était pas un entier dans unList<Integer>
:Parce que ln est un
List<Number>
, y ajouter un Float semble parfaitement légal. Mais si ln avait un alias avecli
, alors cela briserait la promesse de sécurité de type implicite dans la définition de li - que c'est une liste d'entiers, c'est pourquoi les types génériques ne peuvent pas être covariants.la source
ArrayStoreException
à l'exécution.WHY
tableaux sont rendus covariants. comme Sotirios l'a mentionné, avec les tableaux, on obtiendrait ArrayStoreException au moment de l'exécution, si les tableaux étaient rendus invariants, alors nous pourrions détecter cette erreur au moment de la compilation elle-même correcte?Animal
, qui n'a pas à accepter les éléments reçus d'ailleurs" de "Array qui ne doit contenir queAnimal
, et doit être prêt à accepter des références externes àAnimal
. Le code qui a besoin du premier devrait accepter un tableau deCat
, mais le code qui a besoin du second ne devrait pas. Si le compilateur pouvait distinguer les deux types, il pourrait fournir une vérification à la compilation. Malheureusement, la seule chose qui les distingue ...Les tableaux sont covariants pour au moins deux raisons:
Il est utile pour les collections contenant des informations qui ne changeront jamais en covariantes. Pour qu'une collection de T soit covariante, son magasin de support doit également être covariant. Bien que l'on puisse concevoir une
T
collection immuable qui n'utilise pas aT[]
comme magasin de stockage (par exemple en utilisant une arborescence ou une liste chaînée), une telle collection aurait peu de chances de fonctionner aussi bien qu'une collection soutenue par un tableau. On pourrait argumenter qu'une meilleure façon de fournir des collections immuables covariantes aurait été de définir un type de "tableau immuable covariant" qu'ils pourraient utiliser un magasin de sauvegarde, mais autoriser simplement la covariance de tableau était probablement plus facile.Les tableaux seront fréquemment mutés par du code qui ne sait pas quel type de chose va être dedans, mais ne mettra pas dans le tableau tout ce qui n'a pas été lu dans ce même tableau. Un exemple typique de ceci est le code de tri. Conceptuellement, il aurait pu être possible pour les types de tableaux d'inclure des méthodes pour permuter ou permuter des éléments (ces méthodes pourraient être également applicables à n'importe quel type de tableau), ou de définir un objet "manipulateur de tableau" contenant une référence à un tableau et une ou plusieurs choses qui avaient été lus à partir de celui-ci, et pourraient inclure des méthodes pour stocker les éléments précédemment lus dans le tableau d'où ils provenaient. Si les tableaux n'étaient pas covariants, le code utilisateur ne pourrait pas définir un tel type, mais le runtime aurait pu inclure des méthodes spécialisées.
Le fait que les tableaux soient covariants peut être considéré comme un horrible hack, mais dans la plupart des cas, il facilite la création de code fonctionnel.
la source
The fact that arrays are covariant may be viewed as an ugly hack, but in most cases it facilitates the creation of working code.
- bon pointUne caractéristique importante des types paramétriques est la capacité d'écrire des algorithmes polymorphes, c'est-à-dire des algorithmes qui fonctionnent sur une structure de données indépendamment de sa valeur de paramètre, comme
Arrays.sort()
.Avec les génériques, c'est fait avec des types génériques:
Pour être vraiment utiles, les types génériques nécessitent une capture générique, ce qui nécessite la notion de paramètre de type. Rien de tout cela n'était disponible au moment où les tableaux ont été ajoutés à Java, et la création de tableaux de type covariant de référence permettait un moyen beaucoup plus simple d'autoriser des algorithmes polymorphes:
Cependant, cette simplicité a ouvert une faille dans le système de type statique:
nécessitant une vérification à l'exécution de chaque accès en écriture à un tableau de type référence.
En un mot, la nouvelle approche incarnée par les génériques rend le système de type plus complexe, mais aussi plus sûr de type statique, tandis que l'ancienne approche était plus simple et moins sûre de type statique. Les concepteurs du langage ont opté pour une approche plus simple, ayant des choses plus importantes à faire que de fermer une petite faille dans le système de typage qui pose rarement des problèmes. Plus tard, lorsque Java a été établi et que les besoins urgents ont été satisfaits, ils ont eu les ressources pour le faire correctement pour les génériques (mais le changer pour des tableaux aurait cassé les programmes Java existants).
la source
Les génériques sont invariants : à partir de JSL 4.10 :
et quelques lignes plus loin, JLS explique également que les
tableaux sont covariants (première puce):
4.10.3 Sous-typage parmi les types de tableaux
la source
Je pense qu'ils ont pris une mauvaise décision au départ, ce qui a rendu le tableau covariant. Cela brise la sécurité de type telle qu'elle est décrite ici et ils sont restés bloqués avec cela à cause de la compatibilité descendante et après cela, ils ont essayé de ne pas faire la même erreur pour le générique. Et c'est l'une des raisons pour lesquelles Joshua Bloch préfère les listes aux tableaux du point 25 du livre "Effective Java (deuxième édition)"
la source
Ma prise: lorsque le code attend un tableau A [] et que vous lui donnez B [] où B est une sous-classe de A, il n'y a que deux choses à s'inquiéter: que se passe-t-il lorsque vous lisez un élément de tableau, et que se passe-t-il si vous écrivez il. Il n'est donc pas difficile d'écrire des règles de langage pour s'assurer que la sécurité des types est préservée dans tous les cas (la règle principale étant qu'un an
ArrayStoreException
peut être jeté si vous essayez de coller un A dans un B []). Pour un générique, cependant, lorsque vous déclarez une classeSomeClass<T>
, il peut y avoir un certain nombre de façons d'T
utiliser dans le corps de la classe, et je suppose que c'est trop compliqué de travailler sur toutes les combinaisons possibles pour écrire des règles sur quand les choses sont autorisées et quand elles ne le sont pas.la source