Quelle est la différence entre
List<Map<String, String>>
et
List<? extends Map<String, String>>
?
Si il n'y a pas de différence, ce qui est l'avantage de l'utilisation de ? extends
?
Quelle est la différence entre
List<Map<String, String>>
et
List<? extends Map<String, String>>
?
Si il n'y a pas de différence, ce qui est l'avantage de l'utilisation de ? extends
?
La différence est que, par exemple, un
List<HashMap<String,String>>
est un
List<? extends Map<String,String>>
mais pas un
List<Map<String,String>>
Donc:
void withWilds( List<? extends Map<String,String>> foo ){}
void noWilds( List<Map<String,String>> foo ){}
void main( String[] args ){
List<HashMap<String,String>> myMap;
withWilds( myMap ); // Works
noWilds( myMap ); // Compiler error
}
Vous penseriez d'un List
de HashMap
s doit être un List
de Map
s, mais il y a une bonne raison pourquoi il n'est pas:
Supposons que vous pourriez faire:
List<HashMap<String,String>> hashMaps = new ArrayList<HashMap<String,String>>();
List<Map<String,String>> maps = hashMaps; // Won't compile,
// but imagine that it could
Map<String,String> aMap = Collections.singletonMap("foo","bar"); // Not a HashMap
maps.add( aMap ); // Perfectly legal (adding a Map to a List of Maps)
// But maps and hashMaps are the same object, so this should be the same as
hashMaps.add( aMap ); // Should be illegal (aMap is not a HashMap)
C'est pourquoi, une List
de HashMap
s ne devrait pas être un List
de Map
s.
Ce que je suis absent dans les autres réponses, c'est une référence à la façon dont cela se rapporte à la covariance et la contravariance et sous - et aux supertypes (polymorphisme) en général et à Java en particulier. Cela peut être bien comprise par l'OP, mais juste au cas où, ici, il va:
Si vous avez une classe Automobile
, alors Car
et Truck
sont leurs sous-types. Toute Voiture peut être affectée à une variable de type Automobile, c'est bien connu dans les OO et s'appelle le polymorphisme. La Covariance se réfère à l'aide de ce même principe dans les scénarios avec les génériques ou les délégués. Java n'a pas de délégués (encore), de sorte que le terme ne s'applique qu'à des génériques.
J'ai tendance à penser de la covariance comme standard polymorphisme de ce que vous attendez du travail sans y penser, parce que:
List<Car> cars;
List<Automobile> automobiles = cars;
// You'd expect this to work because Car is-a Automobile, but
// throws inconvertible types compile error.
La raison de l'erreur est, cependant, de rectification: List<Car>
ne pas hériter de la List<Automobile>
et ne peut donc pas être attribuée à chacun des autres. Seuls les paramètres de type générique ont une hériter d'une relation. On pourrait penser que le compilateur Java n'est tout simplement pas assez intelligent pour comprendre correctement votre scénario. Cependant, vous pouvez aider le compilateur en lui donnant un indice:
List<Car> cars;
List<? extends Automobile> automobiles = cars; // no error
Le revers de la co-variance est la contravariance. Où dans la covariance les types de paramètre doit avoir un sous-type de relation, dans la contravariance ils doivent avoir un supertype relation. Cela peut être considéré comme un héritage de la limite supérieure: tout supertype est autorisé jusqu'à et y compris le type spécifié:
class AutoColorComparer implements Comparator<Automobile>
public int compare(Automobile a, Automobile b) {
// Return comparison of colors
}
Ceci peut être utilisé avec des Collections.tri:
public static <T> void sort(List<T> list, Comparator<? super T> c)
// Which you can call like this, without errors:
List<Car> cars = getListFromSomewhere();
Collections.sort(cars, new AutoColorComparer());
Vous pouvez même l'appeler avec un comparateur qui compare les objets et les utiliser avec n'importe quel type.
Un peu OT peut-être, vous ne demandez pas, mais il permet de comprendre en répondant à votre question. En général, lorsque vous obtenez quelque chose, l'utilisation de la covariance et quand vous mettez quelque chose, utilisez la contravariance. C'est mieux expliqué dans une réponse à un Débordement de Pile question Comment la contravariance être utilisé en Java génériques?.
List<? extends Map<String, String>>
Vous utilisez extends
, de sorte que les règles de la covariance s'applique. Ici vous avez une liste de cartes et chaque article que vous stockez dans la liste doit être un Map<string, string>
ou en dérivent. L'énoncé List<Map<String, String>>
ne peut pas dériver de Map
, mais doit être un Map
.
Par conséquent, le travail, parce qu' TreeMap
hérite Map
:
List<Map<String, String>> mapList = new ArrayList<Map<String, String>>();
mapList.add(new TreeMap<String, String>());
mais ce ne sera pas:
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new TreeMap<String, String>());
et cela ne fonctionne pas non plus, car elle ne satisfait pas la covariance contrainte:
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new ArrayList<String>()); // This is NOT allowed, List does not implement Map
C'est sans doute évident, mais vous avez peut-être déjà noté que l'utilisation de l' extends
mot-clé s'applique seulement à ce paramètre et pas pour le reste. I. e., le suivant ne compile pas:
List<? extends Map<String, String>> mapList = new List<? extends Map<String, String>>();
mapList.add(new TreeMap<String, Element>()) // This is NOT allowed
Supposons que vous voulez permettre à tous les type de la carte, avec une clé comme une chaîne de caractères, vous pouvez utiliser extend
sur chaque paramètre de type. I. e., supposons que vous processus XML et que vous souhaitez stocker AttrNode, Élément etc dans une carte, vous pouvez faire quelque chose comme:
List<? extends Map<String, ? extends Node>> listOfMapsOfNodes = new...;
// Now you can do:
listOfMapsOfNodes.add(new TreeMap<Sting, Element>());
listOfMapsOfNodes.add(new TreeMap<Sting, CDATASection>());
Aujourd'hui, j'ai utilisé cette fonctionnalité, voici donc ma très frais réels-par exemple la vie. (J'ai changé de classe et de la méthode de noms génériques pour ne pas détourner l'attention du point réel.)
J'ai une méthode qui est censé accepter un Set
de A
des objets que j'ai écrit avec cette signature:
void myMethod(Set<A> set)
Mais voulez-vous vraiment les appeler avec des Set
s de sous-classes de A
. Mais ce n'est pas permis! (La raison en est que, myMethod
pourrait ajouter des objets à l' set
qui sont de type A
, mais pas de sous-type set
'les objets sont déclarés être à l'appelant du site. Donc, cela pourrait briser le système de type, si c'était possible.)
Maintenant, voici venir les génériques à la rescousse, parce qu'il fonctionne comme prévu si j'utilise cette méthode de signature à la place:
<T extends A> void myMethod(Set<T> set)
ou plus courte, si vous n'avez pas besoin d'utiliser le type réel dans le corps de la méthode:
void myMethod(Set<? extends A> set)
De cette façon, set
s'type devient une collection d'objets du réel sous-type de l' A
, de sorte qu'il devient possible d'utiliser cette fonction avec des sous-classes sans mettre en danger le système de type.
Comme vous l'avez mentionné, il pourrait y avoir deux versions de la définition d'une Liste:
List<? extends Map<String, String>>
List<?>
2 est très ouvert. Il peut contenir tout type d'objet. Cela peut ne pas être utile dans le cas où vous voulez avoir une carte d'un type donné. Dans le cas où quelqu'un accidentellement met un autre type de carte, par exemple, Map<String, int>
. Votre méthode peut se rompre.
Afin de s'assurer qu' List
peut contenir des objets d'un type donné, Java génériques introduite ? extends
. Donc, en #1, l' List
peut contenir n'importe quel objet qui est dérivée à partir de Map<String, String>
type. L'ajout de tout autre type de données permettrait de lever une exception.
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.