Ich habe eine List<SubClass>
, die ich als behandeln möchte List<BaseClass>
. Es scheint, dass es kein Problem sein sollte, da das Umwandeln von a SubClass
in a BaseClass
ein Kinderspiel ist, aber mein Compiler beschwert sich, dass das Umwandeln unmöglich ist.
Was ist der beste Weg, um einen Verweis auf dieselben Objekte wie ein zu erhalten List<BaseClass>
?
Im Moment erstelle ich nur eine neue Liste und kopiere die alte Liste:
List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)
Aber so wie ich es verstehe, muss eine völlig neue Liste erstellt werden. Ich möchte, wenn möglich, einen Verweis auf die ursprüngliche Liste!
java
inheritance
casting
Riley Lark
quelle
quelle
Antworten:
Die Syntax für diese Art der Zuweisung verwendet einen Platzhalter:
Es ist wichtig zu erkennen , dass eine
List<SubClass>
ist nicht mit einem austauschbarList<BaseClass>
. Code, der einen Verweis auf enthältList<SubClass>
, erwartet, dass jedes Element in der Liste a istSubClass
. Wenn ein anderer Teil des Codes auf die Liste als a verweist,List<BaseClass>
beschwert sich der Compiler nicht, wenn einBaseClass
oderAnotherSubClass
eingefügt wird. Dies führt jedoch zu aClassCastException
für den ersten Code, der davon ausgeht, dass alles in der Liste a istSubClass
.Generische Sammlungen verhalten sich nicht wie Arrays in Java. Arrays sind kovariant; das heißt, es ist erlaubt, dies zu tun:
Dies ist zulässig, da das Array den Typ seiner Elemente "kennt". Wenn jemand versucht, etwas zu speichern, das keine Instanz
SubClass
im Array ist (über diebases
Referenz), wird eine Laufzeitausnahme ausgelöst.Generika Sammlungen Sie nicht „wissen“ , deren Komponententyp; Diese Informationen werden beim Kompilieren "gelöscht". Daher können sie keine Laufzeitausnahme auslösen, wenn ein ungültiger Speicher auftritt. Stattdessen wird a
ClassCastException
an einem weit entfernten, schwer zuzuordnenden Punkt im Code ausgelöst, wenn ein Wert aus der Sammlung gelesen wird. Wenn Sie die Compiler-Warnungen zur Typensicherheit beachten, vermeiden Sie diese Typfehler zur Laufzeit.quelle
erickson hat bereits erklärt, warum man das nicht kann, aber hier einige lösungen:
Wenn Sie nur Elemente aus Ihrer Basisliste entfernen möchten, sollte Ihre Empfangsmethode grundsätzlich als a deklariert werden
List<? extends BaseClass>
.Wenn dies nicht
Collections.unmodifiableList(...)
der Fall ist und Sie es nicht ändern können, können Sie die Liste umbrechen, wodurch eine Liste eines Supertyps des Argumentparameters zurückgegeben werden kann. (Es vermeidet das Typensicherheitsproblem, indem UnsupportedOperationException bei Einfügeversuchen ausgelöst wird.)quelle
Wie @erickson erklärte, stellen Sie sicher, dass kein Code etwas in diese Liste einfügt, wenn Sie sie unter der ursprünglichen Deklaration jemals wieder verwenden möchten, wenn Sie wirklich einen Verweis auf die ursprüngliche Liste wünschen. Der einfachste Weg, es zu bekommen, besteht darin, es einfach in eine einfache alte ungenerische Liste umzuwandeln:
Ich würde dies nicht empfehlen, wenn Sie nicht wissen, was mit der Liste passiert, und vorschlagen würden, den Code zu ändern, der die Liste benötigt, um die Liste zu akzeptieren, die Sie haben.
quelle
Unten finden Sie einen nützlichen Ausschnitt, der funktioniert. Es wird eine neue Array-Liste erstellt, aber die Erstellung von JVM-Objekten über Kopf ist unwichtig.
Ich habe gesehen, dass andere Antworten nicht unbedingt kompliziert sind.
quelle
Ich habe die Antwort verpasst, bei der Sie gerade die ursprüngliche Liste mit einer Doppelbesetzung besetzt haben. Der Vollständigkeit halber hier:
Es wird nichts kopiert und der Vorgang ist schnell. Sie betrügen hier jedoch den Compiler, sodass Sie unbedingt darauf achten müssen, die Liste nicht so zu ändern, dass die
subList
Starts Elemente eines anderen Untertyps enthalten. Beim Umgang mit unveränderlichen Listen ist dies normalerweise kein Problem.quelle
List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)
quelle
Was Sie versuchen zu tun, ist sehr nützlich und ich finde, dass ich es sehr oft in Code tun muss, den ich schreibe. Ein Beispiel für einen Anwendungsfall:
Angenommen, wir haben eine Schnittstelle
Foo
und einzorking
Paket, dasZorkingFooManager
Instanzen von package-private erstellt und verwaltetZorkingFoo implements Foo
. (Ein sehr häufiges Szenario.)So
ZorkingFooManager
muss ein enthalten ,private Collection<ZorkingFoo> zorkingFoos
aber es braucht eine belichtenpublic Collection<Foo> getAllFoos()
.Die meisten Java-Programmierer würden nicht zweimal überlegen, bevor sie
getAllFoos()
ein neuesArrayList<Foo>
zuweisen, es mit allen Elementen auszorkingFoos
füllen und es zurückgeben. Ich genieße es, den Gedanken zu unterhalten, dass etwa 30% aller Taktzyklen, die von Java-Code auf Millionen von Computern auf der ganzen Welt verbraucht werden, nichts anderes tun, als solche nutzlosen Kopien von ArrayLists zu erstellen, die nach ihrer Erstellung Mikrosekunden mit Müll gesammelt werden.Die Lösung für dieses Problem besteht natürlich darin, die Sammlung herunterzuwerfen. Hier ist der beste Weg, dies zu tun:
Was uns zur
castList()
Funktion bringt :Die Zwischenvariable
result
ist aufgrund einer Perversion der Java-Sprache erforderlich:return (List<T>)list;
erzeugt eine "ungeprüfte Besetzung" -Ausnahme; So weit, ist es gut; aber dann:@SuppressWarnings( "unchecked" ) return (List<T>)list;
ist eine illegale Verwendung der Annotation "Unterdrückungswarnungen".Obwohl die Verwendung
@SuppressWarnings
für einereturn
Anweisung nicht koscher ist , ist es anscheinend in Ordnung, sie für eine Zuweisung zu verwenden. Die zusätzliche Variable "result" löst dieses Problem. (Es sollte sowieso entweder vom Compiler oder von der JIT optimiert werden.)quelle
So etwas sollte auch funktionieren:
Ich weiß nicht genau, warum in Eclipse Clazz benötigt wird.
quelle
Wie wäre es, alle Elemente zu gießen? Es wird eine neue Liste erstellt, es wird jedoch auf die ursprünglichen Objekte aus der alten Liste verwiesen.
quelle
Dies ist der vollständige Code, der Generics verwendet, um die Unterklassenliste in eine Superklasse umzuwandeln.
Aufrufer-Methode, die den Unterklassentyp übergibt
Callee-Methode, die jeden Subtyp der Basisklasse akzeptiert
quelle