汎用参照で配列を使用できる理由

Why can we use array with generic reference


質問 written by Community @2017-05-23 10:08:40Z

: 6 : 2 : 581

ここでそれについての質問に答えながら: https : //stackoverflow.com/a/9872630/82609

私は次のことをしようとしました:

Comparator<String>[] comparators = new Comparator[] {...};

できます! しかし、以下はそうではありません:

Comparator<String>[] comparators = new Comparator<String>[] {...};

関連する質問について、私は仮定をしました:

最初は配列のコントラクトが次のようなものになる可能性があるためだと思います。

X型の配列を作成する場合、IS-NOT-AN Xであるものをその中に入れることは決してできません。しようとすると、ArrayStoreExceptionが発生します。

したがって、ジェネリックを作成できる配列を許可すると、次のような別のルールになります。

タイプX<Y>配列を作成する場合、IS-NOT-AN Xであるものを置くことは決してできません。しようとすると、ArrayStoreExceptionが発生します。 ただし、型の消去により、 X<Y>およびX<Z>オブジェクトの両方を追加できます!


しかし、それについて考えると、それは本当に問題になるでしょうか?

Comparator<String>[] comparators = new Comparator<String>[] {...};

そのようなものを使用すると次のようになるため、なぜそれが不可能なのか本当に理解していません:

  • 実行時に挿入されたクラスを確認します
  • コンパイル時に挿入されたクラスのタイプを確認します

最後に、ジェネリック型参照を持つ配列を使用できます。ジェネリック型を持つ配列を作成することは不可能であるため、多くの人はそれが可能であることさえ知らないと思います。

誰かがこの選択の背後にある理由を知っているのだろうか?

これは、人々にList<String> = new ArrayList();使用を強制するようなものList<String> = new ArrayList(); List<String> = new ArrayList<String>();を使用する代わりに


dimitrisli、ジョシュア・ブロッホの有名な本から素敵な例をあげました。 あなた/彼が説明したように、ジェネリック配列と共分散の両方を使用することは危険であり、共分散を使用した配列からArrayStoreExceptionを期待している間にClassCastExceptionを引き起こす可能性があります。

ただし、以下が依然として合法であり、同じことにつながることに注意してください。

List<String>[] stringLists = new List[1];
List<Integer> intList = Arrays.asList(42);
Object[] objects = stringLists;
objects[0] = intList;
String s = stringLists[0].get(0);

ただし、コンパイル時に未チェックのキャスト警告が生成され、前述のように、実行時にClassCastExceptionが生成されます。

コメント 1

エラージェネリックアレイ作成の重複の可能性

written by デイブウェッブ @2012-03-27 09:16:25Z

コメント 2

@DaveWebb何が重複していますか?

written by セバスチャンローバー @2012-03-27 09:19:21Z

コメント 3

誰かがすでに同じ質問をしています。stackoverflow.com/questions/3903196/…–

written by デイブウェッブ @2012-03-27 09:24:45Z

コメント 4

@DaveWebbそれはまったく同じ質問ではないと思います-リンクされた質問はコンパイルエラーとその回避方法について尋ねていますが、これは言語設計の背後にある動機付けの哲学について尋ねています。

written by アンドジェジドイル @2012-03-27 09:26:28Z

コメント 5

最初の答えを読んでください。ここで何が起こっているかの詳細な説明へのリンクがあります。

written by デイブウェッブ @2012-03-27 09:32:14Z

回答 1 written by Andrzej Doyle @2012-03-27 09:24:51Z
4

あなたがどこから来たのかはわかりますが(実際には、基本的には同意します)、現在の状況を動機づける違いがあると思います。

既に述べたように、消去とは、実行時に汎用パラメーターが使用できないため、コンパイル時に型がチェックされることを意味します( List<String>またはComparator<String>[] )。 重大なことに、これは変数のジェネリックパラメーターに基づいています

一方、配列は実行時に挿入時に引数の型をチェックするため、誤用された場合(通常は共分散の悪用による) ArrayStoreExceptionをスローできます。 したがって、配列は内部で両方の箇条書きチェックを実行できる必要があり、もちろん実行時にジェネリックパラメーターをチェックすることはできません。 したがって、配列はジェネリックパラメータを完全に無視する必要があるため、ジェネリック配列をインスタンス化しても意味がありません。

そうは言っても、そのような配列をパラメーター化された参照に割り当てることは理にかなっています。コンパイラーが汎用チェックを実行できるからです。 そして、あなたはこれがすべてのベースをカバーし、ジェネリック型がチェックされることを保証すると考えるのは正しいです(変数が正しくパラメーター化されている限り)。

この選択の背後にある最終的な理由、および配列がこの点でコレクションと異なる理由は、配列が挿入されたときに実際に引数の型をチェックする必要があるためです。コレクションは単にあなたの言葉を受け取り、型を許可するからです後でClassCastExceptionにバブルスルーするエラー。

コメント 1

ありがとう、私はあなたがそれを指摘したと思う:問題はおそらく配列の共分散に関するものです

written by セバスチャン・ローバー @2012-03-27 09:32:21Z

回答 2 written by dimitrisli @2012-03-27 09:26:37Z
1

優れた効果的なJava Second Editionページ120からの引用:

汎用配列の作成が違法である理由-コンパイルできません!

List<String>[] stringLists = new List<String>[1]; // (1)
List<Integer> intList = Arrays.asList(42); // (2)
Object[] objects = stringLists; // (3)
objects[0] = intList; // (4)
String s = stringLists[0].get(0); // (5)

汎用配列を作成する1行目が正当であると仮定しましょう。 2行目では、単一の要素を含むList<Integer>を作成して初期化します。 3行目では、 List<String>配列をObject配列変数に格納していますが、これは配列が共変であるため有効です。 4行目は、 List<Integer>をObject配列の唯一の要素に格納します。これは、ジェネリックが消去によって実装されているため成功しますList<Integer>インスタンスのランタイムタイプは単にListであり、 List<String>[]ランタイムタイプList<String>[]インスタンスはList[]であるため、この割り当てはArrayStoreException生成しません。 今、私たちは困っている。 List<Integer>インスタンスを、 List<String>インスタンスのみを保持するように宣言されている配列に格納しました。 5行目では、この配列の唯一のリストから唯一の要素を取得します。 コンパイラは取得した要素を自動的にStringにキャストしますが、これは整数であるため、実行時にClassCastExceptionします。 これを防ぐために、1行目(汎用配列を作成)でコンパイル時エラーが生成されます。

コメント 1

ありがとう、すてきな例、質問を編集した。List <String> [] stringLists = new List [1];を引き続き使用できることに注意してください。代わりに、正常に動作します。許可されている理由を知っていますか?

written by セバスチャンローバー @2012-03-27 09:43:26Z

コメント 2

生の署名List []を使用して作成し、汎用List <String> []に割り当てようとしているため、これは型安全性の警告です。ただし、リスト<String> []に変更すると、説明した状況を回避するためにコンパイラーがエラーを出します。

written by -dimitrisli @2012-03-27 10:13:34Z