Classcast in Java foreach-Schleife
-
28-09-2019 - |
Frage
Unter welchen Umständen kann Classcast im Code auftritt unten:
import java.util.Arrays;
import java.util.List;
public class Generics {
static List getObjects() {
return Arrays.asList(1, 2, 3);
}
public static void main(String[] args) {
List<String> list = getObjects();
for (Object o : list) { // ClassCastException?
System.out.println(o);
}
}
}
Wir hatten einen ähnlichen Fall in einer Produktionsumgebung (schlechte Praxis, ich weiß) und der Kunde ein Protokoll mit Classcast an der Linie mit dem Kommentar versehen, aber ich kann es nicht zu reproduzieren scheint. Irgendwelche Gedanken?
Ich weiß, dass der JVM einen Iterator im Hintergrund erstellt, wenn foreach verwenden, aber es kann ein rohes Iterator in einigen Fällen und ein parametrisierte man in anderen Fällen erstellen?
Aktualisieren : ich auch einen Blick auf den Bytecode hatte erzeugt und auf Windows, mit JDK 1.6.0_21-b07 no checkcast gemacht wurde. Interessant :)
Hier ist die wichtigste Methode:
public static void main(java.lang.String[]); Code: 0: invokestatic #34; //Method getObjects:()Ljava/util/List; 3: astore_1 4: aload_1 5: invokeinterface #36, 1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 10: astore_3 11: goto 28 14: aload_3 15: invokeinterface #42, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 20: astore_2 21: getstatic #48; //Field java/lang/System.out:Ljava/io/PrintStream; 24: aload_2 25: invokevirtual #54; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V 28: aload_3 29: invokeinterface #60, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z 34: ifne 14 37: return
Danke alle für die Antworten!
Update 2 : Ich habe verleiten mit Eclipse-IDE, die verwendet seine eigene Compiler so tatsächlich der Bytecode oben ist es das man die Eclipse-Compiler . Schauen Sie hier , wie kompilieren manuell Code mit Eclipse. Abschließend erzeugen Eclipse-Compiler unterschiedlichen Byte-Code aus den Sun-Compiler in einigen Fällen, unabhängig von der Plattform, die hier beschriebenen Fall einer zu sein.
Lösung
Should nicht, dass Code immer ein ClassCastException
werfen? Es tut mir die Sun Java 6-Compiler und Laufzeit (unter Linux) verwendet wird. Sie Casting Integer
s als String
s. Der Iterator erstellt wird eine Iterator<String>
sein, aber dann versucht es das erste Element zuzugreifen, das ein Integer
ist, und so ist es nicht.
Dies wird klarer, wenn Sie Ihre Array ändern wie folgt:
return Arrays.asList("one", 2, 3);
Nun ist die Schleife funktioniert tatsächlich für das erste Element, weil das erste Element ein String
ist und wir sehen den Ausgang; dann die Iterator<String>
auf dem zweiten fehlschlägt, weil es nicht eine Zeichenfolge ist.
Ihr Code funktioniert, wenn Sie nur eine generische List
anstelle eines spezifischen verwenden:
List list = getObjects();
for (Object o : list) {
System.out.println(o);
}
... oder natürlich, wenn Sie List<Integer>
verwenden, da der Inhalt Integer
s. Was Sie tun, jetzt löst eine Compiler-Warnung - Note: Generics.java uses unchecked or unsafe operations.
-. Und das aus gutem Grund
Diese Änderung auch funktioniert:
for (Object o : (List)list)
... vermutlich, weil zu diesem Zeitpunkt Sie es zu tun mit einem Iterator
, kein Iterator<String>
.
bozho hat gesagt, er diesen Fehler nicht auf Windows XP nicht sehen (nicht erwähnt, welche Compiler und Laufzeit, aber ich vermute, Sun), und Sie sagen, Sie sehen es nicht (oder nicht zuverlässig), so klar ist, dass eine Umsetzung Empfindlichkeit hier, aber das Endergebnis ist: Do not List<String>
zu interact mit einem List
von Integer
s verwenden. : -)
Hier ist die Datei, die ich bin Kompilieren:
import java.util.Arrays;
import java.util.List;
public class Generics {
static List getObjects() {
return Arrays.asList("one", 2, 3);
}
public static void main(String[] args) {
List<String> list = getObjects();
for (Object o : list) { // ClassCastException?
System.out.println(o);
}
}
}
Hier ist die Zusammenstellung:
tjc@forge:~/temp$ javac Generics.java Note: Generics.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.
Hier ist der Lauf:
tjc@forge:~/temp$ java Generics one Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String at Generics.main(Generics.java:12)
Zeile 12 ist die for
Aussage. Beachten Sie, dass es das erste Element hat Ausgang, weil ich, dass auf eine String
geändert. Es hat die andere nicht ausgegeben. (Und bevor ich diese Änderung vorgenommen hat, es scheiterte sofort.)
Hier ist der Compiler verwende ich:
tjc@forge:~/temp$ which javac /usr/bin/javac tjc@forge:~/temp$ ll /usr/bin/javac lrwxrwxrwx 1 root root 23 2010-09-30 16:37 /usr/bin/javac -> /etc/alternatives/javac* tjc@forge:~/temp$ ll /etc/alternatives/javac lrwxrwxrwx 1 root root 33 2010-09-30 16:37 /etc/alternatives/javac -> /usr/lib/jvm/java-6-sun/bin/javac*
Hier ist die Demontage, die die checkcast
zeigt:
tjc@forge:~/temp$ javap -c Generics Compiled from "Generics.java" public class Generics extends java.lang.Object{ public Generics(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return static java.util.List getObjects(); Code: 0: iconst_3 1: anewarray #2; //class java/io/Serializable 4: dup 5: iconst_0 6: ldc #3; //String one 8: aastore 9: dup 10: iconst_1 11: iconst_2 12: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 15: aastore 16: dup 17: iconst_2 18: iconst_3 19: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 22: aastore 23: invokestatic #5; //Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List; 26: areturn public static void main(java.lang.String[]); Code: 0: invokestatic #6; //Method getObjects:()Ljava/util/List; 3: astore_1 4: aload_1 5: invokeinterface #7, 1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 10: astore_2 11: aload_2 12: invokeinterface #8, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z 17: ifeq 40 20: aload_2 21: invokeinterface #9, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 26: checkcast #10; //class java/lang/String 29: astore_3 30: getstatic #11; //Field java/lang/System.out:Ljava/io/PrintStream; 33: aload_3 34: invokevirtual #12; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V 37: goto 11 40: return }
Auch hier, obwohl, hat unter dem Strich sein: Verwenden Sie keine List<String>
zu interact mit einem List
verwenden, die Dinge enthält, die nicht String
s sind. : -)
Andere Tipps
Ich kann es auch nicht reproduzieren, aber ich folgende Fehler erkennen, die behoben werden müssen:
- make
getObjects()
RückkehrList<Integer>
, anstatt eine rohe Art - Sie nicht erwarten
List<String>
, sondern eineList<Integer>
statt - Wenn Iterieren, Schleife
for (Integer o : list)
Das Problem ist die Methode static List getObjects() {
gibt einen allgemeinen (nicht parametrisierte) List
. Und Sie weisen es List<String>
. Diese Linie sollte eine Compiler-Warnung geben, und das soll ein Problem gemeldet hat.
Dieser Teil:
List<String> list = getObjects();
for (Object o : list) { // ClassCastException?
System.out.println(o);
}
Wird vereinfacht werden
List<String> list = getObjects();
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
Object o = iterator.next();
System.out.println(o);
}
Aber die Umsetzung des Iterator wird gegossen versuchen, wenn sie von der next()
in einem Iterator
der String
Methode den Inhalt send.
Aus diesem Grund haben Sie einen CCE.
Lösungen:
Entweder überall Verwendung Generika oder benutzt sie nicht, aber es ist wirklich wichtig consistant zu sein. Wenn Sie eine List<Integer>
oder ein List<? super Integer>
zurückgekehrt war würden Sie dieses Problem bei der Kompilierung gesehen haben.