Ich kann viele Antworten sehen, die zeigen, wie das Problem zu lösen ist, aber nur <a href="https://stackoverflow.com/a/5374346/1393766">Stephens Antwort </a>versucht zu erklären, warum das Problem auftritt, also werde ich versuchen, etwas mehr zu diesem Thema hinzuzufügen. Es ist eine Geschichte über mögliche Gründe, warum <code>Object[] toArray</code> wurde nicht geändert in <code>T[] toArray</code> wo die Generika in Java eingeführt wurden.
Warum String[] stockArr = (String[]) stock_list.toArray();
nicht funktionieren wird?
In Java, der generische Typ existiert nur zur Kompilierzeit . Zur Laufzeit werden Informationen über den generischen Typ (wie in Ihrem Fall <String>
) wird entfernt und ersetzt durch Object
Typ (siehe auch Typenlöschung ). Aus diesem Grund wird zur Laufzeit toArray()
haben keine Ahnung, welchen genauen Typ sie für die Erstellung eines neuen Arrays verwenden sollen, daher verwendet es Object
als sicherster Typ, da jede Klasse Object erweitert und somit Instanzen jeder Klasse sicher speichern kann.
Das Problem ist nun, dass man keine Instanz von Object[]
a String[]
.
Warum? Schauen Sie sich dieses Beispiel an (gehen wir davon aus, dass class B extends A
):
//B extends A
A a = new A();
B b = (B)a;
Obwohl ein solcher Code kompiliert werden kann, werden wir zur Laufzeit sehen, dass die ClassCastException
weil die Instanz als Referenz gehalten wird a
ist eigentlich nicht vom Typ B
(oder seine Subtypen). Warum ist dieses Problem (warum diese Ausnahme muss gegossen werden)? Einer der Gründe ist, dass B
könnte neue Methoden/Felder haben, die A
nicht, so dass es möglich ist, dass jemand versuchen wird, diese neuen Mitglieder über b
Referenz, auch wenn die gehaltene Instanz sie nicht hat (nicht unterstützt). Mit anderen Worten, wir könnten am Ende versuchen, Daten zu verwenden, die nicht existieren, was zu vielen Problemen führen könnte. Um eine solche Situation zu verhindern, löst die JVM eine Ausnahme aus und stoppt weiteren potenziell gefährlichen Code.
Sie könnten jetzt fragen: "Warum werden wir nicht noch früher gestoppt? Warum ist Code, der ein solches Casting beinhaltet, überhaupt kompilierbar? Sollte der Compiler ihn nicht stoppen?". Die Antwort ist: Nein, weil der Compiler nicht sicher wissen kann, was der tatsächliche Typ der Instanz ist, die von a
Referenz, und es besteht die Möglichkeit, dass sie eine Instanz der Klasse B
die die Schnittstelle von b
Referenz. Schauen Sie sich dieses Beispiel an:
A a = new B();
// ^------ Here reference "a" holds instance of type B
B b = (B)a; // so now casting is safe, now JVM is sure that `b` reference can
// safely access all members of B class
Kehren wir nun zu den Arrays zurück. Wie Sie in der Frage sehen, können wir keine Instanz von Object[]
Array zu einem genaueren Typ String[]
wie
Object[] arr = new Object[] { "ab", "cd" };
String[] arr2 = (String[]) arr;//ClassCastException will be thrown
Hier ist das Problem ein wenig anders. Jetzt sind wir sicher, dass String[]
Array hat keine zusätzlichen Felder oder Methoden, da jedes Array nur diese unterstützt:
[]
Betreiber,
length
eingereicht,
- Methoden, die vom Supertyp Object geerbt wurden,
Es ist also no Array-Schnittstelle, die es unmöglich macht. Das Problem ist, dass Object[]
Array neben Strings
kann beliebige Objekte speichern (zum Beispiel Integers
), so dass es möglich ist, dass wir eines schönen Tages mit dem Versuch enden, eine Methode wie strArray[i].substring(1,3)
auf Instanz von Integer
die eine solche Methode nicht kennt.
Um also die sicher dass diese Situation niemals passieren, können in Java Array-Referenzen nur
- Instanzen eines Arrays vom gleichen Typ wie die Referenz (Referenz
String[] strArr
kann halten String[]
)
- Instanzen von Array des Subtyps (
Object[]
kann halten String[]
porque String
ist ein Subtyp von Object
),
aber nicht halten kann
- Array vom Supertyp des Arraytyps von Referenz (
String[]
kann nicht halten Object[]
)
- Array eines Typs, der nicht mit dem Typ der Referenz verwandt ist (
Integer[]
kann nicht halten String[]
)
Mit anderen Worten: So etwas wie das hier ist in Ordnung
Object[] arr = new String[] { "ab", "cd" }; //OK - because
// ^^^^^^^^ `arr` holds array of subtype of Object (String)
String[] arr2 = (String[]) arr; //OK - `arr2` reference will hold same array of same type as
// reference
Man könnte sagen, dass ein Weg, um dieses Problem zu lösen ist, um zur Laufzeit die meisten gemeinsamen Typ zwischen allen Elementen der Liste zu finden und erstellen Sie Array dieses Typs, aber das funktioniert nicht in Situationen, in denen alle Elemente der Liste wird von einem Typ abgeleitet von generischen einer sein. Schauen Sie sich das an
//B extends A
List<A> elements = new ArrayList<A>();
elements.add(new B());
elements.add(new B());
Die häufigste Art ist nun B
no A
also toArray()
A[] arr = elements.toArray();
würde ein Array von B
Klasse new B[]
. Das Problem bei diesem Array ist, dass der Compiler es zwar erlaubt, den Inhalt zu bearbeiten, indem man new A()
Element hinzufügen, würden Sie Folgendes erhalten ArrayStoreException
porque B[]
Array kann nur Elemente der Klasse B
oder ihrer Unterklasse, um sicherzustellen, dass alle Elemente die Schnittstelle von B
, aber Instanz von A
haben möglicherweise nicht alle Methoden/Felder von B
. Diese Lösung ist also nicht perfekt.
Die beste Lösung für dieses Problem ist, explizit anzugeben, welche Art von Array toArray()
sollte zurückgegeben werden, indem dieser Typ als Methodenargument übergeben wird wie
String[] arr = list.toArray(new String[list.size()]);
または
String[] arr = list.toArray(new String[0]); //if size of array is smaller then list it will be automatically adjusted.