Gibt es einen Unterschied zwischen
List<Map<String, String>>
und
List<? extends Map<String, String>>
?
Wenn es keinen Unterschied gibt, welchen Nutzen hat die Verwendung ? extends
?
java
generics
inheritance
polymorphism
Eng.Fouad
quelle
quelle
Antworten:
Der Unterschied besteht darin, dass zum Beispiel a
ist ein
aber nicht a
So:
Sie würden denken, ein
List
vonHashMap
s sollte einList
vonMap
s sein, aber es gibt einen guten Grund, warum es nicht so ist:Angenommen, Sie könnten Folgendes tun:
Deshalb sollte ein
List
vonHashMap
s keinList
vonMap
s sein.quelle
HashMap
liegtMap
es am Polymorphismus.List
vonHashMap
s ist keinList
vonMap
s.List<Map<String,String>> maps = hashMaps;
und deklarierenHashMap<String,String> aMap = new HashMap<String, String>();
, feststellen werden, dass diesmaps.add(aMap);
illegal ist, solangehashMaps.add(aMap);
es legal ist. Der Zweck ist es, das Hinzufügen falscher Typen zu verhindern, aber das Hinzufügen von richtigen Typen wird nicht zugelassen (der Compiler kann den "richtigen" Typ während der Kompilierungszeit nicht bestimmen)HashMap
um eine Liste vonMap
s, die beide Ihre Beispiele sind legal, wenn ich sie richtig gerade lese.Sie können keine Ausdrücke mit Typen wie
List<NavigableMap<String,String>>
dem ersten zuweisen .(Wenn Sie wissen möchten, warum Sie nicht zuweisen können
List<String>
,List<Object>
dass zig andere Fragen zu SO angezeigt werden .)quelle
List<String>
ist kein Subtyp vonList<Object>
? - siehe zum Beispiel stackoverflow.com/questions/3246137/…? extends
. Es erklärt auch nicht die Korrelation mit Super- / Subtypen oder Co- / Kontravarianz (falls vorhanden).Was mir in den anderen Antworten fehlt, ist ein Hinweis darauf, wie sich dies auf Co- 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 ist es:
Kovarianz
Wenn Sie eine Klasse haben
Automobile
, dannCar
undTruck
sind ihre Untertypen. Jedes Auto kann einer Variablen vom Typ Automobil zugeordnet werden. Dies ist in OO bekannt und wird als Polymorphismus bezeichnet. Kovarianz bezieht sich auf die Verwendung des gleichen Prinzips in Szenarien mit Generika oder Delegierten. 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:
Der Grund des Fehlers ist jedoch richtig: erbt
List<Car>
nicht vonList<Automobile>
und kann daher nicht einander zugeordnet werden. Nur die generischen Typparameter haben eine Vererbungsbeziehung. Man könnte denken, dass der Java-Compiler einfach nicht klug genug ist, um Ihr Szenario dort richtig zu verstehen. Sie können dem Compiler jedoch helfen, indem Sie ihm einen Hinweis geben:Kontravarianz
Die Umkehrung der Kovarianz ist die Kontravarianz. Während in der Kovarianz die Parametertypen eine Subtypbeziehung haben müssen, müssen sie in der Kontravarianz eine Supertypbeziehung haben. Dies kann als Vererbungsobergrenze betrachtet werden: Jeder Supertyp ist zulässig und enthält den angegebenen Typ:
Dies kann mit Collections.sort verwendet werden :
Sie können es sogar mit einem Vergleicher aufrufen, der Objekte vergleicht und mit einem beliebigen Typ verwendet.
Wann sollte Kontra oder Co-Varianz verwendet werden?
Ein bisschen OT vielleicht haben Sie nicht gefragt, aber es hilft zu verstehen, Ihre Frage zu beantworten. Wenn Sie etwas bekommen , verwenden Sie im Allgemeinen Kovarianz und wenn Sie etwas setzen , verwenden Sie Kontravarianz. Dies lässt sich am besten in einer Antwort auf die Frage zum Stapelüberlauf erklären. Wie würde Kontravarianz in Java-Generika verwendet? .
Also was ist es dann mit
List<? extends Map<String, String>>
Sie verwenden
extends
, daher gelten die Regeln für die Kovarianz . Hier haben Sie eine Liste von Karten und jedes Element, das Sie in der Liste speichern, muss einMap<string, string>
oder davon abgeleitet sein. Die AussageList<Map<String, String>>
kann ableiten nicht ausMap
, sondern müssen einMap
.Daher funktioniert Folgendes, da
TreeMap
erbt vonMap
:aber das wird nicht:
und dies wird auch nicht funktionieren, da es die Kovarianzbedingung nicht erfüllt:
Was sonst?
Dies ist wahrscheinlich offensichtlich, aber Sie haben möglicherweise bereits bemerkt, dass die Verwendung des
extends
Schlüsselworts nur für diesen Parameter und nicht für den Rest gilt. Dh folgendes wird nicht kompiliert:Angenommen, Sie möchten einen beliebigen Typ in der Map zulassen, mit einem Schlüssel als Zeichenfolge, den Sie
extend
für jeden Typparameter verwenden können. Angenommen, Sie verarbeiten XML und möchten AttrNode, Element usw. in einer Map speichern. Sie können Folgendes tun:quelle
List<? extends Map<String, String>> mapList = new ArrayList<? extends Map<String, String>>(); mapList.add(new TreeMap<String, String>());
führt zufound: ? 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.Heute habe ich diese Funktion verwendet. Hier ist mein sehr frisches Beispiel aus dem wirklichen Leben. (Ich habe Klassen- und Methodennamen in generische Namen geändert, damit sie nicht vom eigentlichen Punkt ablenken.)
Ich habe eine Methode , die eine akzeptieren ist gemeint
Set
vonA
Objekten , dass ich ursprünglich mit dieser Unterschrift schrieb:Aber es will es tatsächlich mit
Set
s von Unterklassen von nennenA
. Das ist aber nicht erlaubt! (Der Grund dafür ist,myMethod
dass Objekteset
vom Typ hinzugefügt werden könntenA
, jedoch nicht vom Subtypset
Objekte als beim Standort des Anrufers deklariert sind. Dies könnte also das Typsystem beschädigen, wenn dies möglich wäre.)Jetzt kommen hier Generika zur Rettung, denn es funktioniert wie beabsichtigt, wenn ich stattdessen diese Methodensignatur verwende:
oder kürzer, wenn Sie den tatsächlichen Typ im Methodenkörper nicht verwenden müssen:
Auf diese Weise wird
set
der Typ zu einer Sammlung von Objekten des tatsächlichen Subtyps vonA
, sodass es möglich wird, dies mit Unterklassen zu verwenden, ohne das Typsystem zu gefährden.quelle
Wie Sie bereits erwähnt haben, gibt es zwei Möglichkeiten, eine Liste zu definieren:
List<? extends Map<String, String>>
List<?>
2 ist sehr offen. Es kann jeden Objekttyp enthalten. Dies ist möglicherweise nicht hilfreich, wenn Sie eine Karte eines bestimmten Typs haben möchten. Für den Fall, dass jemand versehentlich einen anderen Kartentyp einfügt, z
Map<String, int>
. Ihre Verbrauchermethode könnte brechen.Um sicherzustellen, dass
List
Objekte eines bestimmten Typs gespeichert werden können, wurden Java-Generika eingeführt? extends
. In # 1List
kann das also jedes Objekt enthalten, das vomMap<String, String>
Typ abgeleitet ist . Das Hinzufügen eines anderen Datentyps würde eine Ausnahme auslösen.quelle