0

私はJava(および英語)にまったく慣れていないので、ご容赦ください。

のようなものを書いてみました...

Container con = new Container<Book>();
con.insert(new Book());
con.insert(new Car());

...そして、どのような種類のエラーも発生しませんでした。しかし、次のような行...

Car c = con.remove(); // removes the last inserted element for simplicity

「エラー:互換性のないタイプ」と言ったので、に変更しました

Object carObj = (Car) con.remove();

そしてそれはうまくいきました。私の問題は:私が言うとき

new Container<Book>();

Book 型のオブジェクトのみを保持できるコンテナーを作成しましたが、ポインター (ジェネリックではありませんか?) のおかげで、突然、任意の種類のオブジェクトをコンテナーに配置できてしまいました。ここで何が起こったのですか?ポインターは、コンテナー内にあるオブジェクトのパーソナリティのみを認識しますが、主にジェネリックとして作成されたコンテナー内のオブジェクトのパーソナリティを持つすべてのオブジェクトをポインターが許可することを知りませんでした (私の定式化は間違っている可能性があります)。非ジェネリック ポインターがある場合、ジェネリックまたは非ジェネリックのどちらのコンテナーを作成するかは問題ではありませんか? 常に非ジェネリック コンテナーと見なされます (オブジェクトを削除するときにオブジェクトをキャストする必要があります)。

new Container<Book>().insert(new Car()); // compiler error as excepted

好奇心をそそられ、問題をさらに悪化させました(おそらく)。

Container<Car> cars = new Container();
cars.insert(new Book()); // compiler error: required Car, found Book

これで、ポインターはコンテナー内の車のパーソナリティのみを認識します。しかし、コンテナを非ジェネリックとして作成したにもかかわらず、ブックに入れることはできません。なんで?

new Container().insert(new Car()); // works fine

言わなければならない、それは魅力的であり、刺激的でもあります...

4

3 に答える 3

1

参照を操作しています。参照の型は、コンパイル時に使用されるものです。aまたは aを aに挿入しても問題がないように、 a に a を挿入するBookことContainer<Car>は明らかに間違っています。BookCarContainer

同様に、参照が であるときにa が aContainer.removeを返すと期待するのは正しくありません。返されるオブジェクトが a であると期待する理由がないからです。Car<Container>CarBook

于 2013-02-02T17:11:50.707 に答える
1

あなたのコンテナは生のコンテナであり、一般的なコンテナではありません。として宣言されていContainerます。として宣言する必要がありますContainer<Book>

完了したら、行

con.insert(new Car());

もうコンパイルされません。

Java では、オブジェクトのジェネリック型はコンパイル時のみのものです。実行時には、消去により、単なるコンテナになります。したがって、コンテナを として宣言しないContainer<Book>と、生のコンテナが作成され、コンパイラは内部に格納するオブジェクトのタイプについて何もチェックしません。

より明確にするために(少なくともそう願っています)、次の行

Container con = new Container<Book>();

と同等です

Container con = (Container) (new Container<Book>());

Container<Book>aへの参照を生の Container への参照に変換し、そのタイプ セーフを台無しにします。

于 2013-02-02T17:17:08.993 に答える
0

Java は、ジェネリックを使用せずに記述された多くのコードが既にあった後、ジェネリックを追加しました。Listジェネリックの設計者は、標準ライブラリ コンテナーをに変更したいと考えていましたが、すべての use を uses にする必要がある場合、コンパイルできなくなるようList<X>な単純なコードが既にたくさん書かれていました。さらに、彼らは、たとえば を要求し、を渡すことができるインスタンスのみが含まれていることを期待する、独自のレガシー ライブラリを使用している人々を望んでいました。ListList<X>ListStringList<String>

彼らがこれに対処した方法は、すべてのジェネリック型に「生の型」の概念を導入することでした。これは基本的に、ジェネリック型の後に山かっこを書かないことによって得られる型です (たとえばList、 の代わりにList<String>)。生の型には、非生の同等のものとは異なる型チェック規則があります。たとえば、 a がある場合、Listそれはそれに合法的ですが、add何かを戻すと、ダウンキャストする必要があるとして返されますが、 aはaのみを許可しますが、代わりにダウンキャストする必要はありません'バックを取得するための結果。ObjectgetObjectList<String>addStringgetString

残念ながら、言語設計者が作成した下位互換性の規則により、次のような式を書くことができました。

List rawList = new ArrayList<String>();
rawList.add(new PeanutButterSandwich());

コンパイル時エラーなし。次のような悪いこともできます

List<String> stringList = new ArrayList<String>();
stringList.add("string");
List rawList = stringList;
List<PeanutButterSandwich> sandwichList = (List<PeanutButterSandwich>) rawList;
PeanutButterSandwich sandwich = sandwichList.get(0);

これはコンパイルされます(チェックされていないキャストに関する警告が表示されます。つまり、未加工の型からジェネリック型へのキャストを行ったが、コンパイラはそれが正当であることを認識していません)。もちろん、位置 0 の要素はStringではなく であるため、このコードは実行時に間違いなく例外を発生させますPeanutButterSandwich

覚えておくべき重要なことは、生の型は下位互換性のためだけのものであり、新しいコードでは使用しないでください。また、未加工の型を扱っている場合は、それらをジェネリック型にキャストするときに十分に注意してください。コンパイラは間違ったことをするのを止めることはできないからです。

于 2013-02-02T19:28:03.020 に答える