Angenommen, Sie haben zwei Klassen:
public class TestA {}
public class TestB extends TestA{}
Ich habe eine Methode, die a zurückgibt, List<TestA>
und ich möchte alle Objekte in dieser Liste TestB
so umwandeln, dass ich am Ende a habe List<TestB>
.
Das Casting von Generika ist nicht möglich. Wenn Sie die Liste jedoch auf andere Weise definieren, können Sie TestB darin speichern:
Sie müssen noch die Typprüfung durchführen, wenn Sie die Objekte in der Liste verwenden.
quelle
Cannot instantiate the type ArrayList<? extends TestA>
.Sie können wirklich nicht *:
Ein Beispiel stammt aus diesem Java-Tutorial
Angenommen, es gibt zwei Arten
A
undB
solcheB extends A
. Dann ist der folgende Code korrekt:Der vorherige Code ist gültig, da
B
es sich um eine Unterklasse von handeltA
. Was passiert nun mitList<A>
undList<B>
?Es stellt sich heraus, dass dies
List<B>
keine Unterklasse von ist,List<A>
daher können wir nicht schreibenAußerdem können wir nicht einmal schreiben
*: Um das Casting zu ermöglichen, benötigen wir ein gemeinsames Elternteil für beide
List<A>
undList<B>
:List<?>
zum Beispiel. Folgendes ist gültig:Sie erhalten jedoch eine Warnung. Sie können es unterdrücken, indem Sie es
@SuppressWarnings("unchecked")
zu Ihrer Methode hinzufügen .quelle
Mit Java 8 können Sie tatsächlich
quelle
new List<TestB>
, einer Schleife und einer Besetzung?Ich denke, Sie werfen in die falsche Richtung ... Wenn die Methode eine Liste von
TestA
Objekten zurückgibt , ist es wirklich nicht sicher, sie zu werfenTestB
.Grundsätzlich bitten Sie den Compiler,
TestB
Operationen für einen Typ auszuführenTestA
, der diese nicht unterstützt.quelle
Da dies eine weit verbreitete Frage ist und die aktuellen Antworten hauptsächlich erklären, warum sie nicht funktioniert (oder hackige, gefährliche Lösungen vorschlagen, die ich niemals im Produktionscode sehen würde), halte ich es für angebracht, eine weitere Antwort hinzuzufügen, die zeigt die Fallstricke und eine mögliche Lösung.
Der Grund, warum dies im Allgemeinen nicht funktioniert, wurde bereits in anderen Antworten aufgezeigt: Ob die Konvertierung tatsächlich gültig ist oder nicht, hängt von den Typen der Objekte ab, die in der ursprünglichen Liste enthalten sind. Wenn es Objekte in der Liste , dessen Typs nicht vom Typ
TestB
, aber von einer anderen UnterklasseTestA
, dann ist die Besetzung nicht gültig.Natürlich sind die Abgüsse können gültig sein. Manchmal haben Sie Informationen zu den Typen, die für den Compiler nicht verfügbar sind. In diesen Fällen ist es möglich, die Listen zu erstellen, im Allgemeinen wird jedoch nicht empfohlen :
Man könnte entweder ...
Die Implikationen des ersten Ansatzes (der der aktuell akzeptierten Antwort entspricht) sind subtil. Auf den ersten Blick scheint es richtig zu funktionieren. Wenn die Eingabeliste jedoch falsche Typen enthält
ClassCastException
, wird a möglicherweise an einer völlig anderen Stelle im Code ausgegeben, und es kann schwierig sein, dies zu debuggen und herauszufinden, wo das falsche Element in die Liste aufgenommen wurde. Das schlimmste Problem ist, dass jemand möglicherweise sogar die ungültigen Elemente hinzufügt, nachdem die Liste umgewandelt wurde, was das Debuggen noch schwieriger macht.Das Problem des Debuggens dieser Fehler
ClassCastExceptions
kann mit derCollections#checkedCollection
Methodenfamilie behoben werden.Filtern der Liste nach Typ
Eine typsicherere Methode zum Konvertieren von a
List<Supertype>
nach aList<Subtype>
besteht darin , die Liste tatsächlich zu filtern und eine neue Liste zu erstellen, die nur Elemente mit einem bestimmten Typ enthält. Es gibt einige Freiheitsgrade für die Implementierung einer solchen Methode (z. B. in Bezug auf die Behandlung vonnull
Einträgen), aber eine mögliche Implementierung kann folgendermaßen aussehen:Diese Methode kann verwendet werden, um beliebige Listen zu filtern (nicht nur mit einer bestimmten Subtyp-Supertyp-Beziehung bezüglich der Typparameter), wie in diesem Beispiel:
quelle
Sie können nicht besetzen
List<TestB>
,List<TestA>
wie Steve Kuo erwähnt, ABER Sie können den Inhalt vonList<TestA>
in ablegenList<TestB>
. Versuche Folgendes:Ich habe diesen Code nicht ausprobiert, daher gibt es wahrscheinlich Fehler, aber die Idee ist, dass er durch das Datenobjekt iteriert und die Elemente (TestB-Objekte) zur Liste hinzufügt. Ich hoffe das funktioniert bei dir.
quelle
Der beste sichere Weg besteht darin,
AbstractList
Elemente zu implementieren und in die Implementierung umzuwandeln. Ich habe eineListUtil
Hilfsklasse erstellt :Sie können die
cast
Methode verwenden, um Objekte in der Liste blind zu konvertieren, und dieconvert
Methode zum sicheren Casting. Beispiel:quelle
Der einzige Weg, den ich kenne, ist das Kopieren:
quelle
Wenn Sie eine Objektreferenz umwandeln, umwandeln Sie nur den Typ der Referenz, nicht den Typ des Objekts. Durch das Casting wird der tatsächliche Objekttyp nicht geändert.
Java hat keine impliziten Regeln zum Konvertieren von Objekttypen. (Im Gegensatz zu Primitiven)
Stattdessen müssen Sie angeben, wie ein Typ in einen anderen konvertiert und manuell aufgerufen werden soll.
Dies ist ausführlicher als in einer Sprache mit direkter Unterstützung, aber es funktioniert und Sie sollten dies nicht sehr oft tun müssen.
quelle
Wenn Sie ein Objekt der Klasse haben
TestA
, können Sie es nicht umwandelnTestB
. JederTestB
ist einTestA
, aber nicht der andere Weg.im folgenden Code:
Die zweite Zeile würde a werfen
ClassCastException
.Sie können eine
TestA
Referenz nur umwandeln, wenn das Objekt selbst istTestB
. beispielsweise:Daher können Sie eine Liste von nicht immer in eine Liste von
TestA
umwandelnTestB
.quelle
Sie können die
selectInstances
Methode in Eclipse-Sammlungen verwenden . Dies beinhaltet die Erstellung einer neuen Kollektion, ist jedoch nicht so effizient wie die akzeptierte Lösung, die Casting verwendet.Ich habe
StringBuffer
in das Beispiel aufgenommen, um zu zeigen, dassselectInstances
nicht nur der Typ heruntergespielt wird, sondern auch gefiltert wird, wenn die Sammlung gemischte Typen enthält.Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.
quelle
Dies ist aufgrund der Löschung möglich. Sie werden das finden
Intern sind beide Listen vom Typ
List<Object>
. Aus diesem Grund kann man nicht eins zum anderen werfen - es gibt nichts zu wirken.quelle
Integer j = 42; Integer i = (Integer) j;
funktioniert gut, beide sind ganze Zahlen, beide haben dieselbe Klasse, also "gibt es nichts zu wirken".Das Problem ist, dass Ihre Methode KEINE Liste von TestA zurückgibt, wenn sie einen TestB enthält. Was ist also, wenn sie korrekt eingegeben wurde? Dann diese Besetzung:
funktioniert so gut, wie Sie es sich erhoffen können (Eclipse warnt Sie vor einer ungeprüften Besetzung, die genau das ist, was Sie tun, also meh). Können Sie dies nutzen, um Ihr Problem zu lösen? Eigentlich können Sie deshalb:
Genau das, wonach Sie gefragt haben, oder? oder um genau zu sein:
scheint für mich ganz gut zu kompilieren.
quelle
Dieser Test zeigt , wie zu werfen
List<Object>
zuList<MyClass>
. Sie müssen jedoch darauf achten, dassobjectList
Instanzen des gleichen Typs wie enthalten sindMyClass
. Und dieses Beispiel kann berücksichtigt werden, wennList<T>
es verwendet wird. Holen Sie sich zu diesem Zweck das FeldClass<T> clazz
in den Konstruktor und verwenden Sie es anstelle vonMyClass.class
.quelle
Das sollte funktionieren
quelle
Es ist ziemlich seltsam, dass das manuelle Casting einer Liste immer noch nicht von einer Toolbox bereitgestellt wird, die Folgendes implementiert:
Natürlich werden die Elemente nicht einzeln überprüft, aber genau das möchten wir hier vermeiden, wenn wir wissen, dass unsere Implementierung nur den Untertyp bereitstellt.
quelle