Aufführen> vs Liste>

Lesezeit: 10 Minuten

Benutzer-Avatar
Eng.Fouad

Gibt es einen Unterschied zw

List<Map<String, String>>

und

List<? extends Map<String, String>>

?

Wenn es keinen Unterschied gibt, was ist der Vorteil der Verwendung ? extends?

  • Ich liebe Java, aber das ist eines der Dinge, die nicht so gut sind …

    – Milbe Mitreski

    22. März 2012 um 9:23 Uhr

  • Ich habe das Gefühl, dass es klar wird, wenn wir es wie “alles, was sich ausdehnt …” lesen.

    – Müll

    22. März 2012 um 11:18 Uhr

  • Unglaublich, über 12.000 Aufrufe in etwa 3 Tagen?!!

    – Eng.Fouad

    25. März 2012 um 11:44 Uhr

  • Es erreichte die Titelseite von Hacker News. Herzlichen Glückwunsch.

    – Chinmoy

    25. März 2012 um 19:07 Uhr

  • @Eng.Gefunden hier ist es. Nicht mehr auf der Titelseite, aber gestern da. news.ycombinator.net/item?id=3751901 (gestern war Sonntagmittag hier in Indien.)

    – Chinmoy

    26. März 2012 um 7:37 Uhr

Benutzer-Avatar
Wahrhaftigkeit

Der Unterschied besteht darin, dass z. B. a

List<HashMap<String,String>>

ist ein

List<? extends Map<String,String>>

aber nicht ein

List<Map<String,String>>

So:

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
}

Sie würden denken, a List von HashMaps sollte a sein List von Maps, aber es gibt einen guten Grund, warum es nicht so ist:

Angenommen, Sie könnten Folgendes tun:

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)

Deshalb also a List von HashMaps sollte nicht a sein List von Maps.

  • Still, HashMap ist ein Map aufgrund des Polymorphismus.

    – Eng.Fouad

    21. März 2012 um 18:22 Uhr


  • Richtig, aber a List von HashMaps ist kein List von Maps.

    – Wahrhaftigkeit

    21. März 2012 um 18:26 Uhr

  • Das nennt man Begrenzte Quantifizierung

    – Dan Burton

    22. März 2012 um 2:28 Uhr

  • Gutes Beispiel. Bemerkenswert ist auch, dass selbst wenn Sie deklarieren List<Map<String,String>> maps = hashMaps; und HashMap<String,String> aMap = new HashMap<String, String>();das wirst du noch finden maps.add(aMap); ist illegal, während hashMaps.add(aMap); ist legal. Der Zweck besteht darin, das Hinzufügen falscher Typen zu verhindern, erlaubt jedoch nicht das Hinzufügen richtiger Typen (der Compiler kann den “richtigen” Typ während der Kompilierzeit nicht bestimmen).

    – Razzia

    22. März 2012 um 5:23 Uhr

  • @Raze nein, eigentlich du kann füge hinzu ein HashMap zu einer Liste von Maps, Ihre beiden Beispiele sind legal, wenn ich sie richtig lese.

    – Wahrhaftigkeit

    22. März 2012 um 18:20 Uhr

Benutzer-Avatar
Tom Hawtin – Tackline

Sie können keine Ausdrücke mit Typen wie z List<NavigableMap<String,String>> zum ersten.

(Wenn Sie wissen möchten, warum Sie nicht zuweisen können List<String> zu List<Object> ein … sehen Millionen andere Fragen zu SO.)

  • kann es näher erklären? Ich kann nicht verstehen oder einen Link für bewährte Verfahren?

    – Samir Mangrolia

    21. März 2012 um 18:23 Uhr

  • @Samir Erklär was? List<String> ist kein Subtyp von List<Object>? – siehe zum Beispiel stackoverflow.com/questions/3246137/…

    – Tom Hawtin – Angelleine

    21. März 2012 um 18:29 Uhr

  • Dies erklärt nicht, was der Unterschied zwischen mit oder ohne ist ? extends. Es erklärt auch nicht die Korrelation mit Super-/Subtypen oder Ko-/Kontravarianz (falls vorhanden).

    – Abel

    28. März 2012 um 7:14 Uhr

Benutzer-Avatar
Abel

Was mir in den anderen Antworten fehlt, ist ein Hinweis darauf, wie sich dies auf Ko- und Kontravarianz sowie Sub- und Supertypen (dh Polymorphismus) im Allgemeinen und auf Java im Besonderen bezieht. Dies mag vom OP gut verstanden werden, aber nur für den Fall, hier geht es:

Kovarianz

Wenn Sie eine Klasse haben Automobiledann Car und Truck sind ihre Unterarten. Jedes Auto kann einer Variablen vom Typ Automobil zugewiesen werden, dies ist in OO bekannt und wird als Polymorphismus bezeichnet. Kovarianz bezieht sich auf die Verwendung desselben Prinzips in Szenarien mit Generika oder Delegaten. Java hat (noch) keine Delegaten, daher gilt der Begriff nur für Generika.

Ich neige dazu, Kovarianz als Standardpolymorphismus zu betrachten, was Sie erwarten würden, ohne nachzudenken, weil:

List<Car> cars;
List<Automobile> automobiles = cars;
// You'd expect this to work because Car is-a Automobile, but
// throws inconvertible types compile error.

Die Fehlerursache ist aber richtig: List<Car> nicht geerbt von List<Automobile> und können somit nicht einander zugeordnet werden. Nur die generischen Typparameter haben eine Vererbungsbeziehung. Man könnte meinen, dass der Java-Compiler einfach nicht intelligent genug ist, um Ihr Szenario dort richtig zu verstehen. Sie können dem Compiler jedoch helfen, indem Sie ihm einen Hinweis geben:

List<Car> cars;
List<? extends Automobile> automobiles = cars;   // no error

Kontravarianz

Die Umkehrung der Kovarianz ist die Kontravarianz. Während bei der Kovarianz die Parametertypen eine Subtypbeziehung haben müssen, müssen sie bei der Kontravarianz eine Supertypbeziehung haben. Dies kann als Obergrenze für die Vererbung betrachtet werden: Jeder Supertyp ist zulässig, einschließlich des angegebenen Typs:

class AutoColorComparer implements Comparator<Automobile>
    public int compare(Automobile a, Automobile b) {
        // Return comparison of colors
    }

Dies kann mit verwendet werden Sammlungen.sort:

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

Sie könnten es sogar mit einem Vergleicher aufrufen, der Objekte vergleicht, und es mit jedem beliebigen Typ verwenden.

Wann ist Kontra- oder Kovarianz zu verwenden?

Ein bisschen OT vielleicht, Sie haben nicht gefragt, aber es hilft beim Verständnis, Ihre Frage zu beantworten. Im Allgemeinen, wenn Sie erhalten etwas, verwenden Sie Kovarianz und wenn Sie stellen etwas, verwenden Sie Kontravarianz. Dies wird am besten in einer Antwort auf die Frage zum Stapelüberlauf erklärt Wie würde Kontravarianz in Java-Generika verwendet werden?.

Also, was ist es dann mit List<? extends Map<String, String>>

Sie nutzen extendsalso die Regeln für Kovarianz gilt. Hier haben Sie eine Liste mit Karten und jedes Element, das Sie in der Liste speichern, muss eine sein Map<string, string> oder davon ableiten. Die Aussage List<Map<String, String>> kann nicht ableiten Mapmuss aber sein a Map.

Daher wird das Folgende funktionieren, weil TreeMap erbt von Map:

List<Map<String, String>> mapList = new ArrayList<Map<String, String>>();
mapList.add(new TreeMap<String, String>());

aber das wird nicht:

List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>();
mapList.add(new TreeMap<String, String>());

und das wird auch nicht funktionieren, weil es die Kovarianzbedingung nicht erfüllt:

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

Was sonst?

Dies ist wahrscheinlich offensichtlich, aber Sie haben vielleicht bereits bemerkt, dass die Verwendung von extends Schlüsselwort gilt nur für diesen Parameter und nicht für den Rest. Das heißt, Folgendes wird nicht kompiliert:

List<? extends Map<String, String>> mapList = new List<? extends Map<String, String>>();
mapList.add(new TreeMap<String, Element>())  // This is NOT allowed

Angenommen, Sie möchten jeden Typ in der Karte mit einem Schlüssel als Zeichenfolge zulassen, den Sie verwenden können extend auf jedem Typparameter. Angenommen, Sie verarbeiten XML und möchten AttrNode, Element usw. in einer Karte speichern, können Sie Folgendes tun:

List<? extends Map<String, ? extends Node>> listOfMapsOfNodes = new...;

// Now you can do:
listOfMapsOfNodes.add(new TreeMap<Sting, Element>());
listOfMapsOfNodes.add(new TreeMap<Sting, CDATASection>());

  • Nichts in “Also, was ist das dann mit …” wird kompiliert.

    – NobleUplift

    9. August 2013 um 21:48 Uhr

  • @NobleUplift: Es ist schwierig, Ihnen zu helfen, wenn Sie die erhaltene Fehlermeldung nicht angeben. Erwägen Sie alternativ auch, eine neue Frage zu SO zu stellen, um Ihre Antwort zu erhalten, größere Erfolgschancen. Der obige Code ist nur ein Ausschnitt, es hängt davon ab, ob Sie ihn in Ihrem Szenario implementiert haben.

    – Abel

    12. August 2013 um 18:36 Uhr

  • Ich habe keine neue Frage, ich habe Verbesserungen. List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>(); mapList.add(new TreeMap<String, String>()); ergibt sich found: ? 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>()); funktioniert perfekt. Das letzte Beispiel ist offensichtlich richtig.

    – NobleUplift

    13. August 2013 um 19:15 Uhr

  • @NobleUplift: Entschuldigung, lange Reise, wird behoben, wenn ich zurück bin, und danke für den Hinweis auf den eklatanten Fehler! 🙂

    – Abel

    17. September 2013 um 11:02 Uhr

  • Kein Problem, ich bin nur froh, dass es behoben wird. Ich habe die Änderungen für Sie vorgenommen, wenn Sie sie genehmigen möchten.

    – NobleUplift

    17. September 2013 um 16:06 Uhr


Benutzer-Avatar
Rörd

Heute habe ich diese Funktion verwendet, also hier ist mein sehr frisches Beispiel aus dem wirklichen Leben. (Ich habe Klassen- und Methodennamen in generische geändert, damit sie nicht vom eigentlichen Punkt ablenken.)

Ich habe eine Methode, die a akzeptieren soll Set von A Objekte, die ich ursprünglich mit dieser Signatur geschrieben habe:

void myMethod(Set<A> set)

Aber es will eigentlich mit telefonieren Sets von Unterklassen von A. Aber das ist nicht erlaubt! (Der Grund dafür ist, myMethod Objekte hinzufügen könnte set die vom Typ sind Aaber nicht vom Subtyp that setDie Objekte von werden als auf der Seite des Aufrufers deklariert. Dies könnte also das Typensystem brechen, wenn es möglich wäre.)

Jetzt kommen Generika zur Rettung, denn es funktioniert wie beabsichtigt, wenn ich stattdessen diese Methodensignatur verwende:

<T extends A> void myMethod(Set<T> set)

oder kürzer, wenn Sie den eigentlichen Typ nicht im Methodentext verwenden müssen:

void myMethod(Set<? extends A> set)

Diesen Weg, setDer Typ von wird zu einer Sammlung von Objekten des tatsächlichen Untertyps von Asodass es möglich wird, dies mit Unterklassen zu verwenden, ohne das Typsystem zu gefährden.

Wie Sie bereits erwähnt haben, könnte es zwei Versionen der Definition einer Liste geben:

  1. List<? extends Map<String, String>>
  2. List<?>

2 ist sehr offen. Es kann jeden Objekttyp aufnehmen. Dies ist möglicherweise nicht nützlich, wenn Sie eine Karte eines bestimmten Typs haben möchten. Falls jemand beispielsweise versehentlich einen anderen Kartentyp einlegt, Map<String, int>. Ihre Verbrauchermethode könnte brechen.

Um das zu gewährleisten List kann Objekte eines bestimmten Typs enthalten, eingeführte Java-Generika ? extends. Also in #1, die List kann jedes Objekt enthalten, das abgeleitet ist Map<String, String> Typ. Das Hinzufügen eines anderen Datentyps würde eine Ausnahme auslösen.

1330250cookie-checkAufführen> vs Liste>

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy