質問で私が言及されたので、私はチャイムを鳴らします。
この配列変数をクラス外に公開しなければ基本的には問題ありません。(ちょっと、ベガスで何が起こるかはベガスにとどまります。)
配列の実際の実行時の型はObject[]
. したがって、それを型の変数に入れることはBar[]
事実上「嘘」Object[]
です。しかし、この嘘はクラス内に消されてしまうので、クラス内に残っていれば大丈夫です。( の下限はこの質問にあります。 の下限が何か他のものである場合、この議論での のすべての出現をその境界が何であれ置き換えます。) しかし、この嘘が何らかの形で外部にさらされた場合 (最も単純な例は、変数を type として直接返している場合、問題が発生します。Bar[]
Object
Bar
Bar
Object
Bar
Object
Bar
Object
bars
Bar[]
実際に何が起こっているのかを理解するには、ジェネリックを使用した場合と使用しない場合のコードを確認することをお勧めします。ジェネリック プログラムは、ジェネリックを削除して適切な場所にキャストを挿入するだけで、同等の非ジェネリック プログラムに書き直すことができます。この変換は型消去と呼ばれます。
Foo<Bar>
配列内の特定の要素を取得および設定するためのメソッドと、配列全体を取得するためのメソッドを使用して、の単純な実装を検討します。
class Foo<Bar> {
Bar[] bars = (Bar[])new Object[5];
public Bar get(int i) {
return bars[i];
}
public void set(int i, Bar x) {
bars[i] = x;
}
public Bar[] getArray() {
return bars;
}
}
// in some method somewhere:
Foo<String> foo = new Foo<String>();
foo.set(2, "hello");
String other = foo.get(3);
String[] allStrings = foo.getArray();
型消去後、これは次のようになります。
class Foo {
Object[] bars = new Object[5];
public Object get(int i) {
return bars[i];
}
public void set(int i, Object x) {
bars[i] = x;
}
public Object[] getArray() {
return bars;
}
}
// in some method somewhere:
Foo foo = new Foo();
foo.set(2, "hello");
String other = (String)foo.get(3);
String[] allStrings = (String[])foo.getArray();
そのため、クラス内にキャストはもうありません。ただし、呼び出しコードにはキャストがあり、1 つの要素を取得するときと、配列全体を取得するときです。1 つの要素を取得するためのキャストは失敗しないはずです。なぜなら、配列に入れることができるBar
のは だけであり、取り出すことができるのも だけだからBar
です。ただし、配列全体を取得するときのキャストは失敗します。これは、配列が実際の実行時の型を持っているためObject[]
です。
非一般的に書かれているので、何が起こっているのか、そして問題はより明白になります。特に厄介なのは、ジェネリックでキャストを記述したクラスではキャストの失敗が発生しないことです。このクラスを使用する他の誰かのコードで発生します。そして、その他人のコードは完全に安全で無害です。また、ジェネリック コードでキャストを行った時点では発生しません。後で誰かが を呼び出したときにgetArray()
、警告なしに発生します。
このメソッドがなければgetArray()
、このクラスは安全です。この方法では、安全ではありません。安全ではない特徴は何ですか?前に作成した「嘘」に依存するbars
type として返されます。Bar[]
嘘は真実ではないので、問題を引き起こします。メソッドが代わりに配列を type として返した場合Object[]
、「嘘」に依存しないため安全です。
人々は、このようなキャストをしないように言うでしょう。なぜなら、チェックされていないキャストがあった元の場所ではなく、上記のように予期しない場所でキャスト例外が発生するからです。コンパイラは、それが安全でないことを警告しません (コンパイラgetArray()
の観点からすると、指定した型が安全であるためです)。したがって、プログラマーがこの落とし穴に注意を払い、危険な方法で使用しないようにする必要があります。
ただし、これは実際には大きな問題ではないと私は主張します。適切に設計された API は、内部インスタンス変数を外部に公開することはありません。(内容を配列として返すメソッドがあっても、内部変数を直接返すことはありません。外部コードが配列を直接変更するのを防ぐために、それをコピーします。)getArray()
とにかく、のように実装されるメソッドはありません。