126 votes

Liste<Map<String, String="">> vs Liste<? extends Map<String, String>>

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?

176voto

trutheality Points 13261

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 HashMaps doit être un List de Maps, 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 HashMaps ne devrait pas être un List de Maps.

25voto

Vous ne pouvez pas affecter des expressions avec des types comme List<NavigableMap<String,String>> à la première.

(Si vous voulez savoir pourquoi vous ne pouvez pas affecter List<String> de List<Object> voir un tas d' autres questions.)

16voto

Abel Points 24335

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:

La Covariance

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

La Contravariance

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.

Lors de l'utilisation de contra ou co-variance?

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?.

Alors, quel est-il alors avec 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

Quoi d'autre?

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>());

4voto

Rörd Points 4728

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 Sets 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, sets'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.

0voto

Funsuk Wangadu Points 101

Comme vous l'avez mentionné, il pourrait y avoir deux versions de la définition d'une Liste:

  1. List<? extends Map<String, String>>
  2. 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.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