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
?
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
?
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 HashMap
s sollte a sein List
von Map
s, 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 HashMap
s sollte nicht a sein List
von Map
s.
Still, HashMap
ist ein Map
aufgrund des Polymorphismus.
– Eng.Fouad
21. März 2012 um 18:22 Uhr
Richtig, aber a List
von HashMap
s ist kein List
von Map
s.
– Wahrhaftigkeit
21. März 2012 um 18:26 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 Map
s, Ihre beiden Beispiele sind legal, wenn ich sie richtig lese.
– Wahrhaftigkeit
22. März 2012 um 18:20 Uhr
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
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:
Wenn Sie eine Klasse haben Automobile
dann 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
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.
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?.
List<? extends Map<String, String>>
Sie nutzen extends
also 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 Map
muss 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
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
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 Set
s 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 A
aber nicht vom Subtyp that set
Die 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, set
Der Typ von wird zu einer Sammlung von Objekten des tatsächlichen Untertyps von A
sodass 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:
List<? extends Map<String, String>>
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.
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