6

以下のコードで 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 を含むログを提供しましたが、再現できないようです。何かご意見は?

foreach を使用する場合、JVM がバックグラウンドでイテレーターを作成することは知っていますが、場合によっては生のイテレーターを作成し、別の場合にはパラメーター化されたイテレーターを作成できますか?

更新:生成されたバイトコードも確認しましたが、Windows では、JDK 1.6.0_21-b07 を使用してチェックキャストが行われませんでした。面白い :)

主な方法は次のとおりです。

public static void main(java.lang.String[]);
  コード:
   0: インボークスタティック #34; //メソッド getObjects:()Ljava/util/List;
   3: アストア_1
   4: aload_1
   5: 呼び出しインターフェイス #36、1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
   10: アストア_3
   11: 後藤28
   14: アロード_3
   15: 呼び出しインターフェイス #42、1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   20: アストア_2
   21: getstatic #48; //フィールド java/lang/System.out:Ljava/io/PrintStream;
   24: アロード_2
   25: インボークバーチャル #54; //メソッド java/io/PrintStream.println:(Ljava/lang/Object;)V
   28: アロード_3
   29: 呼び出しインターフェイス #60、1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   34:イフネ14
   37: 戻る

答えてくれてありがとう!

更新 2 :独自のコンパイラを使用する Eclipse IDE で誤解を招いたため、実際にはその上のバイトコードはEclipse コンパイラを使用して生成されたものです。Eclipse でコードを手動でコンパイルする方法については、こちらを参照してください。結論として、Eclipse コンパイラーは、プラットフォームに関係なく、場合によっては Sun コンパイラーとは異なるバイトコードを生成します。ここで説明するケースはその 1 つです。

4

4 に答える 4

5

そのコードは常にスローすべきではありませんClassCastExceptionか?Sun Java 6 コンパイラーとランタイム (Linux 上) を使用することで実現します。Integers を s としてキャストしていますString。作成されたイテレータは になりますがIterator<String>、最初の要素である にアクセスしようとするIntegerため、失敗します。

配列を次のように変更すると、これはより明確になります。

return Arrays.asList("one", 2, 3);

String最初の要素は aであり、出力が表示されるため、ループは実際には最初の要素に対して機能します。それはIterator<String>文字列ではないため、2番目のもので失敗します。

List特定のコードではなくジェネリックを使用するだけで、コードは機能します。

List list = getObjects();
for (Object o : list) {
    System.out.println(o);
}

...または、もちろん、使用する場合はList<Integer>、内容がIntegers であるためです。あなたが今やっていることは、コンパイラの警告を引き起こします — Note: Generics.java uses unchecked or unsafe operations. — そしてそれには正当な理由があります。

この変更も機能します。

for (Object o : (List)list)

Iterator...おそらく、その時点で ではなくを扱っているためですIterator<String>

bozho は、Windows XP ではこのエラーが表示されないと述べています (どのコンパイラとランタイムについては言及していませんが、私は Sun のものだと推測しています)。ここでは実装の機密性について説明しますが、結論は次のとおりです。 of と対話するために使用しないでList<String>ください。:-)ListInteger

コンパイルしているファイルは次のとおりです。

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
注: Generics.java は、未チェックまたは安全でない操作を使用します。
注: 詳細については、-Xlint:unchecked で再コンパイルしてください。

実行は次のとおりです。

tjc@forge:~/temp$ Javaジェネリック
1
スレッド「メイン」の例外 java.lang.ClassCastException: java.lang.Integer は java.lang.String にキャストできません
    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 23 2010-09-30 16:37 /usr/bin/javac -> /etc/alternatives/javac*
tjc@forge:~/temp$ ll /etc/alternatives/javac
lrwxrwxrwx 1 ルート 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.java」からコンパイル
public class Generics extends java.lang.Object{
パブリック ジェネリック ();
  コード:
   0: aload_0
   1: 特別な #1 を呼び出します。//メソッド java/lang/Object."":()V
   4: 戻る

static java.util.List getObjects();
  コード:
   0: アイコンスト_3
   1: 新しい配列 #2; //クラス java/io/Serializable
   4: 複製
   5: アイコンスト_0
   6: ldc #3; //文字列 1
   8: アストア
   9: 重複
   10: アイコンスト_1
   11: アイコンスト_2
   12: インボークスタティック #4; //メソッド java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   15: アーストア
   16: デュプ
   17: アイコンスト_2
   18: アイコンスト_3
   19: インボークスタティック #4; //メソッド java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   22:アストアー
   23: インボークスタティック #5; //メソッド java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
   26: リターン

public static void main(java.lang.String[]);
  コード:
   0: インボークスタティック #6; //メソッド getObjects:()Ljava/util/List;
   3: アストア_1
   4: aload_1
   5: 呼び出しインターフェイス #7、1; //InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
   10: アストア_2
   11: aload_2
   12: 呼び出しインターフェイス #8、1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   17: ifeq 40
   20: アロード_2
   21: 呼び出しインターフェイス #9、1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   26: チェックキャスト #10; //クラス java/lang/String
   29: アストア_3
   30: getstatic #11; //フィールド java/lang/System.out:Ljava/io/PrintStream;
   33: アロード_3
   34: インボークバーチャル #12; //メソッド java/io/PrintStream.println:(Ljava/lang/Object;)V
   37: 後藤11
   40: 戻る

}

繰り返しになりますが、最終List<String>的には次のListようにする必要がありStringます。:-)

于 2010-10-07T11:12:59.903 に答える
1

どちらも再現できませんが、修正する必要がある次の間違いを見つけました。

  • 生の型ではなく、getObjects()returnを作成しますList<Integer>
  • 期待しないList<String>で、List<Integer>代わりに
  • 反復時、ループfor (Integer o : list)
于 2010-10-07T11:14:14.580 に答える
0

この部分 :

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);
}

ただし、Iteratorの実装はnext()、でコンテンツが送信するメソッドIteratorを呼び出すときにキャストを試みStringます。

それがあなたがCCEを持っている理由です。

ソリューション:

どこでもジェネリックを使用するか、使用しないかのどちらかですが、一貫性を保つことが非常に重要です。List<Integer>またはを返した場合はList<? super Integer>、コンパイル時にこの問題が発生します。

于 2010-10-07T11:20:34.767 に答える
0

問題は、メソッドstatic List getObjects() {がジェネリック (パラメーター化されていない) を返すことListです。そして、それを に割り当てますList<String>。この行はコンパイラの警告を表示するはずであり、それは問題を示しているはずです。

于 2010-10-07T11:15:46.083 に答える