it-swarm.com.de

Liste <Map <String, String >> vs Liste <? erweitert Map <String, String >>

Gibt es einen Unterschied zwischen

List<Map<String, String>>

und

List<? extends Map<String, String>>

?

Wenn es keinen Unterschied gibt, welchen Vorteil hat die Verwendung von ? extends?

125
Eng.Fouad

Der Unterschied ist, dass zum Beispiel a

List<HashMap<String,String>>

ist ein

List<? extends Map<String,String>>

aber nicht a

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, dass ein List von HashMaps ein List von Maps sein sollte, 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 sollte ein List von HashMaps kein List von Maps sein.

176
trutheality

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

(Wenn Sie wissen möchten, warum Sie List<String> Nicht zu List<Object> Zuordnen können, lesen Sie ein zillion andere Fragen zu SO.)

25

Was mir in den anderen Antworten fehlt, ist ein Verweis darauf, wie dies mit Co- und Contravariance sowie Sub- und Supertypen (dh Polymorphismus) im Allgemeinen und mit Java im Besonderen zusammenhängt. Dies Der OP mag das gut verstehen, aber nur für den Fall, hier ist es:

Kovarianz

Wenn Sie eine Klasse Automobile haben, dann sind Car und Truck ihre Untertypen. Jedem Auto kann eine Variable des Typs Automobile zugewiesen werden. Dies ist allgemein bekannt in OO und wird als Polymorphismus bezeichnet. Kovarianz bezieht sich auf die Verwendung desselben Prinzips in Szenarien mit Generika oder Delegierten. Java hat (noch) keine Delegierten, daher gilt der Begriff nur für Generika.

Ich neige dazu, Kovarianz als Standard-Polymorphismus zu betrachten, was man erwarten würde, 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.

Der Grund des Fehlers ist jedoch richtig: List<Car> nicht erben von List<Automobile> Und können sich daher nicht zuordnen. Nur die generischen Typparameter haben eine Vererbungsbeziehung. Man könnte meinen, der Java Compiler ist einfach nicht schlau genug, 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

Zuwiderhandlung

Die Umkehrung der Kovarianz ist die Kontravarianz. Wo in der Kovarianz die Parametertypen eine Subtypbeziehung haben müssen, müssen sie im Gegensatz dazu eine Supertypbeziehung haben. Dies kann als Vererbungsobergrenze 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 Collections.sort verwendet werden:

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önnen es sogar mit einem Vergleicher aufrufen, der Objekte vergleicht und mit jedem Typ verwendet.

Wann soll man Kontra oder Co-Varianz verwenden?

Ein bisschen OT hast du vielleicht nicht gefragt, aber es hilft dir, die Beantwortung deiner Frage zu verstehen. Im Allgemeinen, wenn du bekommst etwas, benutze Kovarianz und wenn du put etwas verwenden, Kontravarianz. Dies lässt sich am besten in eine Antwort auf die Frage zum Stapelüberlauf Wie würde die Zuwiderhandlung in Java Generics? verwendet werden.

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

Wenn Sie extends verwenden, gelten die Regeln für Kovarianz. Hier haben Sie eine Liste von Karten und jedes Element, das Sie in der Liste speichern, muss ein Map<string, string> Sein oder daraus abgeleitet werden. Die Anweisung List<Map<String, String>> Kann nicht von Map abgeleitet werden, sondern muss aMap sein.

Daher funktioniert Folgendes, da TreeMap von Map erbt:

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 dies 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 möglicherweise bereits bemerkt, dass die Verwendung des Schlüsselworts extends nur für diesen Parameter und nicht für den Rest gilt. Das heißt, das Folgende 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 einen beliebigen Typ in der Map zulassen. Mit einem Schlüssel als Zeichenfolge können Sie extend für jeden Typparameter verwenden. Angenommen, Sie verarbeiten XML und möchten AttrNode, Element usw. in einer Map speichern, dann 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>());
16
Abel

Heute habe ich diese Funktion verwendet. Hier ist mein sehr aktuelles Beispiel aus der Praxis. (Ich habe die Klassen- und Methodennamen in generische Namen geändert, damit sie nicht vom eigentlichen Punkt ablenken.)

Ich habe eine Methode, die ein Set von A Objekten akzeptiert, die ich ursprünglich mit dieser Signatur geschrieben habe:

void myMethod(Set<A> set)

Aber es möchte es tatsächlich mit Sets von Unterklassen von A aufrufen. Das ist aber nicht erlaubt! (Der Grund dafür ist, dass myMethod Objekte zu set hinzufügen könnte, die vom Typ A sind, aber nicht vom Subtyp, für den die Objekte von set deklariert sind beim Anrufer vor Ort sein. Dies könnte also das Typensystem zerstören, wenn es möglich wäre.)

Nun kommen hier Generika zur Rettung, denn es funktioniert wie vorgesehen, wenn ich stattdessen diese Methodensignatur verwende:

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

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

void myMethod(Set<? extends A> set)

Auf diese Weise wird der Typ von set zu einer Sammlung von Objekten des tatsächlichen Untertyps von A, sodass dies mit Unterklassen verwendet werden kann, ohne das Typsystem zu gefährden.

4
Rörd

Wie Sie bereits erwähnt haben, kann es im Folgenden zwei Versionen zum Definieren einer Liste geben:

  1. List<? extends Map<String, String>>
  2. 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 erstellt, z. B. Map<String, int>. Ihre Verbrauchermethode könnte kaputt gehen.

Um sicherzustellen, dass List Objekte eines bestimmten Typs enthalten kann, wurde Java Generics eingeführt ? extends. In # 1 wird also das List kann jedes Objekt enthalten, das vom Typ Map<String, String> abgeleitet ist. Das Hinzufügen eines anderen Datentyps würde eine Ausnahme auslösen.

0