Java foreachループのClasscastException
-
28-09-2019 - |
質問
以下のコードでClassCastExceptionがどのような状況で発生するかは次のとおりです。
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);
}
}
}
私たちは生産環境で同様のケースを持っていました(悪い練習、私は知っています)、顧客はコメントでClassCastExceptionでログを提供しましたが、私はそれを再現することはできません。何かご意見は?
JVMは、Foreachを使用するときにバックグラウンドでイテレーターを作成することを知っていますが、場合によっては生のイテレーターを作成し、他の場合にパラメータ化されたものを作成できますか?
アップデート:また、JDK 1.6.0_21-B07を使用して、生成されたバイトコードとWindowsでも見ていました チェックキャスト 作られた。面白い :)
主な方法は次のとおりです。
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
答えをありがとう!
更新2: :私はそれを使用するEclipse IDEで誤解を招きました 独自のコンパイラ したがって、実際には上のバイトコードは、 Eclipseコンパイラ. 。見て ここ Eclipseでコードを手動でコンパイルする方法。結論として、Eclipseコンパイラは、プラットフォームに関係なく、Sunコンパイラから異なるバイトコードを生成します。ここで説明するケースは1つです。
解決
そのコードはすべきではありません いつも 投げる ClassCastException
? Sun Java 6コンパイラとランタイム(Linuxで)を使用しています。あなたはキャストしています Integer
s as String
s。作成されたイテレーターはanになります Iterator<String>
, 、しかし、それは最初の要素にアクセスしようとします。 Integer
, 、そして失敗します。
アレイを次のように変更すると、これは明確になります。
return Arrays.asList("one", 2, 3);
最初の要素は String
そして、出力が見えます。そうして Iterator<String>
文字列ではないため、2番目のもので失敗します。
ジェネリックを使用するだけでコードが機能します List
特定のものの代わりに:
List list = getObjects();
for (Object o : list) {
System.out.println(o);
}
...または、もちろん、使用する場合 List<Integer>
, 、内容はあるので Integer
s。あなたが今していることは、コンパイラ警告をトリガーします - Note: Generics.java uses unchecked or unsafe operations.
- そして正当な理由があります。
この変更も機能します。
for (Object o : (List)list)
...おそらくその時点であなたは Iterator
, 、ではありません Iterator<String>
.
Bozhoは、Windows XPでこのエラーが表示されないと言っています(どのコンパイラとランタイムを言及していませんが、Sunを推測しています)、そしてあなたはそれを見ていない(または確実ではない)ので、明らかにいくつかがあります実装の感度はここにありますが、最終的な行は次のとおりです。 List<String>
と対話する List
の Integer
s。 :-)
これが私がコンパイルしているファイルです:
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);
}
}
}
これが編集です:
tjc@forge:~/temp$ javac Generics.java Note: Generics.java uses unchecked or unsafe operations. Note: Recompile with -Xlint:unchecked for details.
これが実行です:
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)
12行目は次のとおりです for
声明。それが私がそれを変更したので、それが最初の要素を出力したことに注意してください String
. 。他の人には出力されませんでした。 (そして、私がその変更を行う前に、すぐに失敗しました。)
これが私が使用しているコンパイラです:
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*
これが分解です checkcast
:
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 }
繰り返しになりますが、一番下の行は次のとおりです。 List<String>
と対話する List
そうでないものが含まれています String
s。 :-)
他のヒント
私もそれを再現することはできませんが、私は修正する必要がある次の間違いを見つけます:
- 作る
getObjects()
戻るList<Integer>
, 、生のタイプではなく - 期待しないでください
List<String>
, 、しかしaList<Integer>
代わりは - 反復するとき、ループ
for (Integer o : list)
問題は方法です static List getObjects() {
汎用(パラメーターではない)を返します List
. 。そして、あなたはそれを割り当てます List<String>
. 。この行はコンパイラ警告を与えるはずであり、それは問題を示唆しているはずです。
この部分 :
List<String> list = getObjects();
for (Object o : list) { // ClassCastException?
System.out.println(o);
}
として簡素化されます
List<String> list = getObjects();
for (Iterator<String> iterator = list.iterator(); iterator.hasNext();) {
Object o = iterator.next();
System.out.println(o);
}
しかし、イテレーターの実装は、電話をかけるときにキャストしようとします next()
メソッドコンテンツが送信します Iterator
で String
.
だからあなたはCCEを持っています。
ソリューション:
どこでもジェネリックを使用するか、使用しないでくださいが、一貫性を持つことは本当に重要です。あなたが返した場合 List<Integer>
またはa List<? super Integer>
編集時にこの問題を見たでしょう。