Ich habe diese handliche, generische Funktion zum Konvertieren einer Sammlung von Sammlungen in einen einzigen Satz geschrieben:
%Vor%Dann habe ich versucht, es anzurufen:
%Vor%und ich habe den folgenden Fehler erhalten:
%Vor% Ein List
ist jetzt ein Collection
und ein String
ist ein T
. Warum funktioniert das nicht und wie repariere ich es?
Ihre Unterschrift sollte sein:
%Vor% Grundsätzlich ist List<S>
kein Untertyp von List<T>
, nur weil S
ein Untertyp von T
ist. Diese Eigenschaft heißt Kovarianz , und in Java sind generische Typen nicht kovariant (andere Sprachen wie scala enthalten kovariante generische Typen).
Was du getan hast, hat nicht funktioniert, weil es möglich sein sollte, Collection<T>
zu einem Collection<Collection<T>>
hinzuzufügen, also zum Beispiel mit deiner Unterschrift wäre dies eine gültige Implementierung:
Aber dann diese Methode wie folgt aufrufen:
%Vor%Führt das zum selben Problem? NEIN! Der Grund dafür ist, dass der Compiler das Hinzufügen von Objekten zu einer Sammlung, die für einen unbekannten Typ parametrisiert ist, nicht erlaubt:
%Vor% Das Verwenden von Platzhaltern war in erster Linie notwendig, damit Sie Dinge tun konnten, wie Sie es tun wollten, wahrscheinlich am besten anhand der Methode Collection.addAll
, so dass List<Number>.addAll(List<Integer>)
erlaubt wäre:
Nein, ist es nicht.
Ich würde die Deklaration als
ändern %Vor% Zwei generische Typen können nur Subtypen sein, wenn die Typargumente identisch sind (oder mit Platzhaltern, also ist Collection<String>
kein Subtyp von Collection<Object>
. Überprüfen Sie die Subtyping-Abschnitt des Generics Tutorials .
Dies ist eine spezielle Version der allgemeineren Frage "Ist Collection<Circle>
eine Art Collection<Shape>
?"
Die Antwort ist eine (vielleicht überraschende) nein .
Die Argumentation ist in einem C ++ - Kontext in der C ++ - FAQ gut beschrieben . Dies ist eine allgemeine OO-Frage, daher gilt die gleiche allgemeine Argumentation.
Betrachten Sie zum Beispiel ein alternatives Universum, in dem Collection<Circle>
eine Art von Collection<Shape>
ist. In diesem Universum könnten Sie so etwas tun:
Was passiert, wenn das Quadrat, eine Form, zur Sammlung von Formen hinzugefügt wird, was wirklich eine Sammlung von Kreisen ist? Es gibt keine gute Antwort.
Das gleiche gilt für Collection<List<T>>
und Collection<Collection<T>>
. A Collection<List<T>>
ist keine Art von Collection<Collection<T>>
, da nicht substituierbar für Collection<Collection<T>>
ist. A Queue<T>
kann zu einer Sammlung von Sammlungen hinzugefügt werden, aber nicht zu einer Sammlung von List<T>
.
Ich hasse es fast, die richtige Antwort zu posten, weil es so hässlich ist, aber da die drei Top-Antworten das verpasst haben, fühle ich mich gezwungen.
%Vor%Sie haben das richtig gelesen. Zwei "? Erweitert" s. Andernfalls können Sie keine List & lt; Integer & gt; und eine Liste & lt; Double & gt; zusammen, um eine Set & lt; Nummer & gt; zu erhalten, die logischerweise möglich sein sollte.
Sobald Sie Generika in Generika verschachtelt haben, werden die Dinge immer unangenehmer.
Sie können sich vergeben dafür entscheiden, stattdessen die einfachere Antwort zu wählen. :) Nur wissen, dass es nicht immer funktioniert wo es logisch sinnvoll ist.
Mit Google Sammlungen können Sie übrigens Iterables.concat(...)
verwenden, oder ImmutableSet.copyOf(Iterables.concat(...))
, wenn Sie das de benötigen -Duplizieren.
Dies ist die Art von Dingen, die dazu führten, dass Java 1.0 entwickelt wurde, um alle dummen C ++ - Vorlagen zu vereinfachen, die stattfanden. Sie fügen 5 Komplikationsebenen hinzu, um eine blöde Besetzung aus einer Sammlung von Objekten in einen Satz Ihrer spezifischen Instanz zu vermeiden. Ok, wenn du feststellst, dass du überall herumwirfst und deinen Code hässlich machst, aber ich wette, das passiert ungefähr einmal in 500.000 Codezeilen. Ja, es ist gut, dass wir diese technischen Details finden, aber wird Ihr Code wirklich wartungsfähiger, wenn Sie diesen Weg gehen?
Tags und Links java generics covariance collections