Y a-t-il une différence entre
List<Map<String, String>>
et
List<? extends Map<String, String>>
?
S'il n'y a pas de différence, quel est l'avantage d'utiliser ? extends
?
java
generics
inheritance
polymorphism
Ing. Fouad
la source
la source
Réponses:
La différence est que, par exemple, un
est un
mais pas un
Alors:
On pourrait penser que a
List
ofHashMap
s devrait être aList
ofMap
s, mais il y a une bonne raison pour laquelle ce n'est pas le cas:Supposons que vous puissiez faire:
C'est pourquoi a
List
deHashMap
s ne devrait pas être unList
deMap
s.la source
HashMap
c'est unMap
dû au polymorphisme.List
deHashMap
s n'est pas unList
deMap
s.List<Map<String,String>> maps = hashMaps;
etHashMap<String,String> aMap = new HashMap<String, String>();
, vous constaterez toujours quemaps.add(aMap);
c'est illégal tant quehashMaps.add(aMap);
c'est légal. Le but est d'empêcher l'ajout de types incorrects, mais cela n'autorisera pas l'ajout de types corrects (le compilateur ne peut pas déterminer le type «bon» pendant la compilation)HashMap
à une liste deMap
s, vos deux exemples sont légaux, si je les lis correctement.Vous ne pouvez pas attribuer d'expressions avec des types tels que
List<NavigableMap<String,String>>
le premier.(Si vous voulez savoir pourquoi vous ne pouvez pas assigner
List<String>
àList<Object>
voir un million d' autres questions sur SO.)la source
List<String>
n'est pas un sous-type deList<Object>
? - voir, par exemple, stackoverflow.com/questions/3246137/…? extends
. Il n'explique pas non plus la corrélation avec les super / sous-types ou la co / contravariance (s'il y en a).Ce qui me manque dans les autres réponses, c'est une référence à la façon dont cela se rapporte aux co- et contravariances et aux sous-et supertypes (c'est-à-dire au polymorphisme) en général et à Java en particulier. Cela peut être bien compris par l'OP, mais juste au cas où, le voici:
Covariance
Si vous avez une classe
Automobile
, alorsCar
etTruck
sont leurs sous-types. N'importe quelle voiture peut être affectée à une variable de type Automobile, ceci est bien connu en OO et s'appelle polymorphisme. La covariance fait référence à l'utilisation de ce même principe dans des scénarios avec des génériques ou des délégués. Java n'a pas (encore) de délégués, donc le terme s'applique uniquement aux génériques.J'ai tendance à considérer la covariance comme un polymorphisme standard, ce à quoi vous vous attendriez sans réfléchir, car:
La raison de l'erreur est, cependant, correcte: n'hérite
List<Car>
pas deList<Automobile>
et ne peut donc pas être attribuée les uns aux autres. Seuls les paramètres de type générique ont une relation d'héritage. On pourrait penser que le compilateur Java n'est tout simplement pas assez intelligent pour bien comprendre votre scénario. Cependant, vous pouvez aider le compilateur en lui donnant un indice:Contravariance
L'inverse de la co-variance est la contravariance. Là où, dans la covariance, les types de paramètres doivent avoir une relation de sous-type, par contre, ils doivent avoir une relation de supertype. Cela peut être considéré comme une limite supérieure d'héritage: tout supertype est autorisé et inclut le type spécifié:
Cela peut être utilisé avec Collections.sort :
Vous pouvez même l'appeler avec un comparateur qui compare des objets et l'utiliser avec n'importe quel type.
Quand utiliser contra ou co-variance?
Un peu OT peut-être, vous ne l'avez pas demandé, mais cela aide à comprendre la réponse à votre question. En général, lorsque vous obtenez quelque chose, utilisez la covariance et lorsque vous mettez quelque chose, utilisez la contravariance. Ceci est mieux expliqué dans une réponse à la question de Stack Overflow Comment la contravariance serait-elle utilisée dans les génériques Java? .
Alors qu'est-ce que c'est alors avec
List<? extends Map<String, String>>
Vous utilisez
extends
, donc les règles de covariance s'appliquent. Ici, vous avez une liste de cartes et chaque élément que vous stockez dans la liste doit être unMap<string, string>
ou en dériver. L'instructionList<Map<String, String>>
ne peut pas dériver deMap
, mais doit être unMap
.Par conséquent, ce qui suit fonctionnera, car
TreeMap
hérite deMap
:mais ce ne sera pas:
et cela ne fonctionnera pas non plus, car cela ne satisfait pas la contrainte de covariance:
Quoi d'autre?
C'est probablement évident, mais vous avez peut-être déjà remarqué que l'utilisation du
extends
mot - clé ne s'applique qu'à ce paramètre et non au reste. Par exemple, les éléments suivants ne seront pas compilés:Supposons que vous souhaitiez autoriser n'importe quel type dans la carte, avec une clé comme chaîne, que vous pouvez utiliser
extend
sur chaque paramètre de type. Par exemple, supposons que vous traitez du XML et que vous souhaitiez stocker AttrNode, Element, etc. dans une carte, vous pouvez faire quelque chose comme:la source
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>(); mapList.add(new TreeMap<String, String>());
résulte enfound: ? extends java.util.Map<java.lang.String,java.lang.String> required: class or interface without bounds
.List<Map<String, String>> mapList = new ArrayList<Map<String, String>>(); mapList.add(new TreeMap<String, String>());
fonctionne parfaitement. Le dernier exemple est évidemment correct.Aujourd'hui, j'ai utilisé cette fonctionnalité, voici donc mon exemple réel très récent. (J'ai changé les noms de classe et de méthode en noms génériques afin qu'ils ne détournent pas l'attention du point réel.)
J'ai une méthode qui est destinée à accepter un
Set
desA
objets que j'ai initialement écrit avec cette signature:Mais il veut réellement l'appeler avec
Set
des sous-classes deA
. Mais ce n'est pas autorisé! (La raison en est que celamyMethod
pourrait ajouter des objets à ceuxset
qui sont de typeA
, mais pas du sous-type quiset
est déclaré que les objets sont sur le site de l'appelant. Cela pourrait donc casser le système de types si c'était possible.)Maintenant, voici les génériques à la rescousse, car cela fonctionne comme prévu si j'utilise cette signature de méthode à la place:
ou plus court, si vous n'avez pas besoin d'utiliser le type réel dans le corps de la méthode:
De cette façon,
set
le type de s devient une collection d'objets du sous-type réel deA
, il devient donc possible de l'utiliser avec des sous-classes sans mettre en danger le système de types.la source
Comme vous l'avez mentionné, il pourrait y avoir deux versions ci-dessous pour définir une liste:
List<? extends Map<String, String>>
List<?>
2 est très ouvert. Il peut contenir n'importe quel type d'objet. Cela peut ne pas être utile au cas où vous voudriez avoir une carte d'un type donné. Au cas où quelqu'un mettrait accidentellement un autre type de carte, par exemple
Map<String, int>
,. Votre méthode de consommation pourrait casser.Afin de s'assurer que
List
peut contenir des objets d'un type donné, les génériques Java ont été introduits? extends
. Ainsi, dans le n ° 1, leList
peut contenir n'importe quel objet dérivé duMap<String, String>
type. L'ajout de tout autre type de données lèverait une exception.la source