8

これは、私が遭遇している状況を理解するための少し不自然な例ですが、基本的に、実際には整数の配列を含むオブジェクトの配列があり、そのようにキャストしようとしています。この状況をシミュレートするコード スニペットを次に示します。

Object[] foo = new Object[3];
foo[0] = new int[] { 1, 2, 3 };
foo[1] = new int[] { 4, 5, 6 };
foo[2] = new int[] { 7, 8, 9 };
int[][] bar = (int[][]) foo;

これを実行しようとすると、例外が発生しますException in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [[I

foo配列をコピーせずにこれを行う方法はありますか (本番環境では非常に大きくなるため、やりたくないかもしれません)。そうでない場合、なぜですか?

4

3 に答える 3

11

キャストによって Java オブジェクトの組み込み型を変更することはできません。これには配列が含まれます。オブジェクトのタイプは、オブジェクトが割り当てられるときに固定され、変更できません。

Object[]したがって、 toのキャストは機能しint[][]ません。

この場合、 whileint[][]は のサブタイプですがObject[]、実行時に型キャストを許可するにはこれでは不十分です。JLS ( 5.5.3. Checked Casts at Run Time ) の実際のルールは次のとおりです。

「オブジェクトの実行時型 R が、キャスト演算子で指定された型の消去 (§4.6) である型 T と互換性のある代入であるかどうかをチェックするアルゴリズムは次のとおりです。実行時例外がスローされた場合、ですClassCastException。」...

「R が配列型 RC[] を表すクラス、つまり、型 RC のコンポーネントの配列である場合:」

  • 「T が配列型 TC[]、つまり、型 TC のコンポーネントの配列である場合、次のいずれかに該当しない限り、実行時例外がスローされます。」

    • 「TC と RC は同じプリミティブ型です。」

    • 「TC と RC は参照型であり、RC 型は、これらのランタイム規則を再帰的に適用することで TC にキャストできます。」

この場合、TC はint[]RC でObjectあり、にキャストするObject ことはできませんint[]。したがって、例外が発生します。

なぜこれが発生しなければならないのかを説明するために、次の (仮説的な) 例を考えてみましょう。

   Object[] foo = new Object[3];
   foo[0] = new Object();
   int[][] bar = /* foo */
   int[] mystery = bar[0];

の値を「キャスト」できると仮定するとfoo、変数は何をmystery指す必要がありますか? int[]を割り当てていないため、 にすることはできません。Object静的型付けが壊れるため、インスタンスにすることはできません。

実際、「キャスト」は違法でなければなりません。さもなければ、Java の静的型システムは崩壊します。


あなたの例と私の例の違いは、 foo が実際には整数の配列の配列ではないということですが、私の場合はそうです。VM がオブジェクトの「実際の」タイプを認識できないのはなぜですか?

ポイントは、静的型付けは (主に) JVM ではなくコンパイラーによって強制されるということです。JVM は (理論的には) 実行時に型が OK であることを判断できます (そうでない場合は例外をスローします) が、コンパイラは一般に型がどうなるかを判断できないため、これを行うことができません。


残念ながら、私は実際に自分で配列を作成していません。私は別の関数からそれを取得していますが、私が知る限り、それは私が制御できないシリアライゼーションの副作用ですObject[]int[][]

残念ながら、配列をコピーするか、それを として使用しObject[]、要素をキャストしint[]て使用する必要があります。例えば

Object[] foo = new Object[3];
foo[0] = new int[] { 1, 2, 3 };
foo[1] = new int[] { 4, 5, 6 };
foo[2] = new int[] { 7, 8, 9 };
...
int nine = ((int[]) (foo[2]))[2];

Javaには「それが本当にあると信じてint[][]」オプションはありません。

これがシリアライゼーションの副作用である場合、シリアライゼーションは壊れています...ある意味で。確かに、これは標準の Java オブジェクト シリアライゼーション プロトコル/実装では起こりません。/メソッドなどで明示的にオーバーライドしない限り、オブジェクトのタイプが保持されます。readObjectwriteObject

于 2013-09-17T01:35:39.110 に答える
1

アクセスするたびに、各オブジェクトをキャストする必要があります。

    Object[] foo = new Object[3];
    foo[0] = new int[] { 1, 2, 3 };
    foo[1] = new int[] { 4, 5, 6 };
    foo[2] = new int[] { 7, 8, 9 };
    int[] foo2 = (int[])foo[2];
    System.out.println("foo[2,2]="+((int[])foo[2])[2]);

try ... catch内容が int でない可能性があると思われる場合は、これも でラップする必要があります。

編集: コードを簡素化します。

于 2013-09-17T01:39:54.347 に答える
1

オブジェクトの配列であるためfoo、整数の配列だけでなく、何でも含めることができるため、コンパイラーは、それが内部にあることがわかっているという理由だけで、整数の配列にキャストすることを許可しません。

参照を使用する場合int[][] barは、最初に別のint [3][]配列を作成し、その配列にコンテンツをコピーする必要がありますfoo

于 2013-09-17T01:35:30.627 に答える