16

一般に、Java は型安全な言語と見なすことができます。ジェネリックにはいくつかの欠陥があることは知っていますが、最近、これまでにない問題に遭遇しました。それを分解するには:

Object[] objects = new Integer[10];
objects[0] = "Hello World";

期待どおりにコンパイル時エラーが発生することはありません。Array of の宣言は、Object他の何かの配列を指すことを許可しないと思います。ジェネリックでは、次のような奇妙なことは許可されていません。

ArrayList<Object> objs = new ArrayList<Integer>

そして、Javaをだまして何かをさせようとすると

ArrayList<? extends Object> objects = new ArrayList<Integer>

宣言することはできますが、 type のオブジェクトしか追加できませんnull

Java がそのような奇妙な配列の宣言を防がないのはなぜですか?

4

5 に答える 5

13

まず、これはタイプセーフであることを指摘しておきます。

Object[] objects = new Integer[10];
objects[0] = "Hello World";

例外がスローされるためです。(これは静的にタイプセーフではありません...しかし、それはまったく別のステートメントです。)

Javaがこれを許可する理由は歴史的です。Java 5まで、Javaはどの形式のジェネリックもサポートしていませんでした。Goslingは、ジェネリックスを理解してJava 1.0に組み込む時間があれば、そうしていただろうと述べています。

残念ながら、彼らはしませんでした。しかし、彼らはそれでも、次の署名を使用して汎用のソートメソッドのようなものを記述できることを望んでいました。

    void sort(Object[] array, Comparator comp) ...

このメソッドをあらゆる種類のオブジェクト配列(ジェネリックなし)で機能させるには、配列を共変にする必要がありました。String[]つまり、正式な型がである場合に、またはInteger[]を引数として渡すことを合法にするためですObject[]。彼らがそれをしていなかったら、あなたはにコピーし、String[]それObject[]をソートし、そしてそれを元に戻す必要があったでしょう。

于 2012-12-03T14:50:58.747 に答える
7

「レガシーデザイン」以外にこれに対する答えはないと思います。(これは「なぜなら」の言い方がおかしかったことは認めます。)どうにかして最後に示した課題と同等のことを実行できる必要があります。(そうしないと、1.4 より前の Java の言語機能を想定して、手動のアップ/ダウン キャストで大量のコピーを作成することになります)

配列の型セマンティクスが基本的に固定されていた Java 1 では、ジェネリックは使用できず、長い間検討の余地さえありませんでした。そのため、このコンストラクトをタイプ セーフにするために必要な高次の型制約を表現するために利用できるメカニズムはありませんでした。また、Gosling (IIRC はシンプルさのファン) は、コンパイル時のタイプ セーフのこのエッジ ケースを解決することは、言語を複雑にする価値がないと感じました。利用可能なソリューションで。または、実行時に解決策を探すのに十分なチェックを行うことに煩わされませんでした。(結局のところ、言語設計の決定は、少なくともある程度は恣意的なものであり、これに確実に答えることができるのは 1 人だけです。)

于 2012-12-03T14:34:51.527 に答える
4

「しなければならないから」です。

少し詳しく説明するために、次の例を考えてみましょう。

Object[] objects = null;
if (something) {
    objects = new Integer[10];
} else {
    objects = new String[10];
}

では、Java コンパイラはどの割り当てを許可し、どの割り当てを拒否するかをどのように判断するのでしょうか? できません。コンパイル時の型は Object であるため、配列の実行時の型を認識していないという理由だけで、コンパイラは任意の Object を配列に入れることができます。

于 2012-12-03T14:29:37.207 に答える
1

実際に配列の場合、間違ったタイプの要素を追加すると、実行時にArrayStoreExceptionという例外が発生します。この場合はStringです。ジェネリックの場合、そのような例外はありません。リストに間違ったタイプを追加する可能性があるためvery same reason、オブジェクト以外は追加できません。

于 2012-12-03T14:29:01.620 に答える
0

グーグルで見つけたディスカッションがあります

私が見つけた:

まず、配列は型の安全性を壊しません。もしそうなら、配列のアップキャストは実行時に失敗しません。これらは、コンパイラがプログラムのタイプ セーフを証明することを不可能にするため、チェックは実行時まで延期されます。

ここで混乱が生じると思います。文字列はオブジェクトであるため、文字列の配列は明らかにオブジェクトの配列であり、他方では明らかにそうではないからです。答えは、配列の可変性にあります。

配列が不変の場合、不変の String[] は常に不変の Object[] とまったく同じであるため、String[] を Object[] として安全に扱うことができます。

一方、配列が変更可能な場合、通常、String[] を Object[] として扱うのは安全ではありません。

上記のリンクで説明されている「ワイルドカード」手法は、CommonLisp が何年にもわたって行ってきたこととまったく同じです。

(deftype StringArray? () (array String)) ; This is the type of arrays of String 
(deftype ObjectArray? () (array Object)) ; This is the type of arrays of Object 
(subtypep StringArray? ObjectArray?)      ; Is StringArray? a subtype of ObjectArray?? false, true                               ; No, it isn't. (false: it isn't, true: I'm ure) 
(deftype AnyArray? () (array *))         ; This is the type of arrays of anything (subtypep StringArray? AnyArray?)         ; Is StringArray? a subtype of AnyArray??   true, true                                ; Yes, it is. (true: it is, true: I'm sure)
于 2012-12-03T17:01:05.983 に答える