3

誰かがJavaで配列が実際にどのように機能するかを説明してもらえますか?

私は次のコードに驚いた:

        Object test = new Object[2][2];
        Object test2 = new Object[] {
                new Object[2],new Object[2]
        };
        Object test3 = new Object[2][];
        ((Object[])test3)[0] = new Object[2];
        ((Object[])test3)[1] = new Object[2];
        System.out.println(test instanceof Object[]);
        System.out.println(test instanceof Object[][]);
        System.out.println(test2 instanceof Object[]);
        System.out.println(test2 instanceof Object[][]);
        System.out.println(test3 instanceof Object[]);
        System.out.println(test3 instanceof Object[][]);

test2のみがObject[][]のインスタンスではありません

実行時の違いは何ですか?

編集:私はいくつかの答えを見ます。ジョンスキート、私ができることに注意してください:

Object[] test4 = (Object [])test;
test4[0] = "blaaa";
test4[1] = "toto";
System.out.println(test4);

test instanceof Object []はtrueを返し、キャストの実行時に例外は発生しません。Sierra&BatesのSCJPの本によると、IS-A Object [] []だけでなく、Object[]もテストしてください。

しかし、 "test4 [0] =" blaaa ";"を使用して新しい値を再割り当てしようとすると、例外が発生します。スレッド "main"の例外java.lang.ArrayStoreException:Main.main(Main。 java:24)

したがって、実行時に、testとtest2の両方がIS-A Object []であり、両方にオブ​​ジェクト配列が含まれているように見えますが、そのうちの1つだけがIS-A Object[][]です。

4

6 に答える 6

12

Test2は、オブジェクトの配列としてのみ宣言されています。そこに含まれるオブジェクトもたまたま配列ですが、これは宣言されていません。それが違いです。

于 2011-06-16T21:09:35.440 に答える
8

test22つの要素の配列を指します。そのタイプはちょうどObject[]-なので、これらの要素は任意のオブジェクトを参照できます。特に、次のように書くことができます。

// Valid
Object[] foo = (Object[]) test2;
foo[0] = "hello";

一方、それは機能しませtest

// Invalid - test isn't just an Object[], it's an Object[][]
Object[] foo = (Object[]) test;
test[0] = "hello";

test参照する配列の要素タイプがではObject[]なくであるためObjectです。配列は、各要素がnullまたはへの参照であることを「認識」しているObject[]ため、VMは文字列を格納できないようにします。

aを-に変換testするObject[]のと同じ方法でに変換できます。これは配列共分散と呼ばれますが、私の意見ではそれを許可するのは間違いでした。これまで見てきたように、VMは実行時にストアをチェックする必要があります。String[]Object[]

于 2011-06-16T21:10:55.393 に答える
2

私はそれをするという私の質問に対する完全な答えを見つけられません...

SCJPの本を読み終えた後、それは私にはずっとはっきりしています。配列ではなく、ジェネリックの章で扱われました。(ジェネリックvsアレイ)Jon Skeetの答えは良いですが、私には不完全なようです。

したがって、ジェネリックスと配列の違いを理解する必要があります。

ジェネリックは単なる「コンパイルセキュリティ」です。実行時のチェックはありません。これは、次のトリックによって、文字列オブジェクトをセットに挿入できることを意味します

public static void main(String [] args) {
    Set<Integer> set = new HashSet<Integer>();
    set.add(1);
    set.add(2);
    addString(set,"test");
    for ( Object o : (Set)set ) {
        System.out.println(o);
    }
    for ( Object o : set ) {
        System.out.println(o);
    }
}

public static void addString(Set set,String s) {
    set.add(s);
}

出力は次のとおりです。

1
2
test
1
2
ClassCastException

http://www.ideone.com/nOSQz

Set<Integer>文字列を整数にキャストしようとする暗黙のキャストがあるため、参照でそのセットを使用するまで、コードは(警告付きで)コンパイルされ、完全に正常に実行されることに注意してください。

これは、下位互換性などの多くの理由(すべてが本に公開されているわけではありません)、および非汎用コレクションを提供しながらレガシーコード(非汎用)を呼び出すことができる必要があるため、この方法で行われました。

レガシーコードを呼び出さず、ジェネリックコードのみを呼び出す場合、そのような問題は発生しないはずです。コレクションには、コンパイラが処理したために必要なものだけが含まれ、実行時にチェックを行う必要はありません...

ジェネリックスには実行時にチェックがなく、コレクションに間違ったアイテムが挿入されないようにするため、パラメータを使用しList<Dog>てaをにキャストしたり、 )List<Animal>を呼び出したりすることは禁止されています。これは、 byにCatを挿入できることを意味するためです。操作..。method(List<Animal>List<Dog>List<Dog>List<Animal>


配列は同じようには機能しません。

配列には、Jon Skeet saisのように、共分散型があります。

Dogは動物なので、Dog[]をAnimal[]に変換できます。

ジェネリックスの場合と同様に、JavaはCatsをDog配列に挿入しないようにします。ただし、共分散のため、コンパイル時に実行することはできません。Jon Skeetのように、この共分散はおそらく良い考えではないことに同意しますが、これはjavaのレガシーです...したがって、ジェネリックスとは対照的に、配列には、犬の配列に猫が挿入されないようにするランタイムチェックがあります。

実行時に、JVMは配列に何を挿入する必要があるかを認識していますが、ジェネリックでは認識していません。


それで、私の最初の質問に戻ります

Object test = new Object[2][2];
Object[] test2 = (Object [])test;
test2[0] = "blaaa";
test2[1] = "toto";
System.out.println(test2);

test(2D配列)はtest2(1D配列)にキャストできますが、舞台裏ではまだ2D配列であり、最終的には、オブジェクトを含む他の1D配列A2で満たされることを期待する1D配列A1です。

そのため、A1(最終的にtest2)で文字列を挿入しようとすると、ArrayStoreExceptionが発生します。文字列は、最終的にはオブジェクトですが、Object[]ではありません。


結論として:

1D配列にキャストできる1Dおよび2D配列を使用しているため、少し混乱する可能性がありますが、このコードでもまったく同じです。

配列:

Dog[] dogs = new Dog[1];
dogs[0] = new Dog();
Animal[] animals = (Animal [])dogs;
animals[1] = new Cat();

これは、実行時に4行目で失敗します。そして、猫を犬の配列に挿入する方法はありません。

そして、ジェネリックで同じことをすると

Set<Dog> dogs = new HashSet<Dog>();
dogs.add( new Dog() );
Set<Animal> animals = (Set<Animal>) dogs;
animals.add( new Cat() );

3行目のため、これはコンパイルされません。しかし、レガシージェネリックコードを使用することで、犬のセットに猫を挿入できます。

于 2011-11-16T10:47:05.500 に答える
1

test2が参照するオブジェクトはObject[]です。

Instanceofは、配列の内容のタイプではなく、test2によって参照されるオブジェクトのタイプをテストしています。

実行時の配列の内容はObject[]であり、Object []はObjectであるため、Object[]に収まります。

于 2011-06-16T21:12:32.613 に答える
0

あなたは次のように定義test2しました

Object test2 = new Object[];     // This is a plain array of Objects.
于 2011-06-16T21:18:37.417 に答える
0

test2Objectを介して作成されたため、何でも入れることができますnew Object[]Object[]sをtest入れることができるのtest3は、より厳密なコンストラクターを介して作成されたためですnew Object[][]

于 2011-06-16T21:10:24.067 に答える