リストオブジェクトの継承とキャスト
-
28-10-2019 - |
質問
私はリストに含まれるフルーツサブクラスに果物のリストをキャストするのに苦労しています。
public class Response {
private List<Fruit> mFruitList;
public List<Fruit> getFruitList() {
return mFruitList;
}
}
public class Fruit {
}
public class Orange extends Fruit {
}
List<Fruit> oranges = response.getFruitList();
クラスオレンジのリストになるようにオレンジをキャストするにはどうすればよいですか?これは悪いデザインパターンですか?基本的に、私は果物のリストであるサーバーからJSON応答を取得しています。 Webサービスへの特定の呼び出しごとに、私はどのフルーツのサブクラスを手に入れるかを知っているので、そのリストを適切にキャストする必要があります。
解決
特定の各呼び出しで知っている場合、果物のサブクラスが得られるものである場合は、キャスティングリストの代わりにジェネリックを使用する必要があります。
public class Response<T extends Fruit> {
private List<T> mFruitList;
public List<T> getFruitList() {
return mFruitList;
}
}
Response<Orange> response = // create
List<Orange> oranges = response.getFruitList();
編集:テンプレートでは、一般的なタイプを意味しました。申し訳ありませんが、最近はC ++が多すぎました
他のヒント
タイプキャストの背後にある全体のアイデアは、コンパイラに「ねえ、私はあなたよりもこれについてもっと知っている」と伝えることができることです。あなたのコードでは、コンパイラは安全にダウンキャストできません List<Fruit>
に List<Orange>
リストに実行時に何が含まれるかがわからないからです。
もしあなたが〜なら 絶対に確実です リストがのみになること Orange
インスタンス、そしてそれはあなたのコードをより管理しやすくし、それのために行きます。
List<Orange> oranges = (List<Orange>) response.getFruitList();
もちろん、コンパイラは警告を発します。あなたは何かをしているので、そうすべきではないと思うからです。そして、JVMが、 CastClassException
あなたが間違っていたら!
リストに含まれるオブジェクトの種類については、ゲートのようなジェネリックを考えてください。この継承と鋳造のために、あなたが期待する方法では機能しません。あなたが与えた例では、オレンジとリンゴの両方をあなたの中に入れることができます
. 。リストにリンゴとオレンジの両方がある場合は、どうすればそれをキャストできますか List<Fruit>
. List<Orange>
必要な場合
それでは、なぜ気にするのか List<Orange>
. 。とにかく明示的にそれをキャストしている場合、それがおそらく不必要な抽象化を含んでいるものを正確に知っている場合。 List<Fruit>
APIで作業している場合は変更できませんが、それが何を含んでいるかを正確に知っている場合は、確認して、それぞれを説明するためだけにチェックのインスタンスをループしてループする必要があります Fruit
インスタンスへ Orange
に必要なとき Orange
API。
リストをキャストし、各要素があるかどうかをテストする必要があります instanceof Orange
, 、およびオレンジでテストキャストの後。これは「最高の練習」です。
与えられた
List<Fruit> getFruits() {...}
タイプキャストすることはできません
List<Orange> oranges = (List<Orange>) getFruits();
タイプの消去により、実行時にgetFruitsのタイプは単なるリストです。コンパイラはあなたにダウンキャストをさえさえさえさえしません(私は疑いがありましたので、回答する前にEclipseで試しました)。
コンパイラに、リストにはフルーツのサブクラスが含まれていることを伝えることができます。その場合、WildCardを使用する必要があります。
List<? extends Fruit> getFruits() {...}
その後、キャストは可能になりますが、タイプの安全警告があります:
@SuppressWarnings("unchecked")
List<Orange> oranges = (List<Orange>) getFruits();
getFruitsのランタイムタイプがあることを考えると は リスト、ジェネリックタイプの情報を破棄して、安全でない同種を使用できます。
@SuppressWarnings("unchecked")
List<Orange> oranges = (List) getFruits();
より多くのシステムリソースを必要とすることは次のようになりますが、それはあなたの意図を明確に述べているので、よりエレガントな方法かもしれません。
List<Orange> oranges = Arrays.asList((Orange[])getFruits().toArray())
Javaの配列は、実行時にタイプ情報を保存するため、キャストはコンパイラの観点から有効で「安全」ですが、フルーツバスケットにリンゴを渡すとランタイム例外をスローできます。