List<SubClass>
として扱いたいList<BaseClass>
があります。 SubClass
をBaseClass
にキャストするのは簡単なので、問題にならないように思えますが、私のコンパイラはキャストが不可能だと文句を言います。
では、List<BaseClass>
と同じオブジェクトへの参照を取得する最良の方法は何ですか?
現在、私は新しいリストを作成し、古いリストをコピーしています。
List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)
しかし、私が理解しているように、完全に新しいリストを作成する必要があります。可能であれば、元のリストへの参照をお願いします!
この種の割り当ての構文では、ワイルドカードを使用します。
List<SubClass> subs = ...;
List<? extends BaseClass> bases = subs;
List<SubClass>
はnotとList<BaseClass>
と交換可能であることを認識することが重要です。 List<SubClass>
への参照を保持するコードは、リスト内のすべてのアイテムがSubClass
であると想定します。コードの別の部分がList<BaseClass>
としてリストを参照した場合、コンパイラはBaseClass
またはAnotherSubClass
が挿入されても文句を言いません。ただし、これにより、コードの最初の部分にClassCastException
が発生し、リスト内のすべてがSubClass
であると想定されます。
ジェネリックコレクションは、Javaの配列と同じようには動作しません。配列は共変です。つまり、次のことが許可されています。
SubClass[] subs = ...;
BaseClass[] bases = subs;
配列は要素のタイプを「知っている」ため、これは許可されます。 SubClass
のインスタンスではない何かを(bases
参照を介して)配列に格納しようとすると、ランタイム例外がスローされます。
ジェネリックコレクションは、notコンポーネントタイプを「認識」します。この情報はコンパイル時に「消去」されます。したがって、無効なストアが発生したときにランタイム例外を発生させることはできません。代わりに、コレクションから値が読み取られるときに、コード内のはるかに離れた、関連付けが困難なポイントでClassCastException
が発生します。型の安全性に関するコンパイラの警告に注意すると、実行時にこれらの型エラーを回避できます。
エリクソンはあなたがこれをできない理由をすでに説明しましたが、ここでいくつかの解決策があります:
基本リストから要素のみを取得する場合、原則として、受信メソッドは_List<? extends BaseClass>
_を取得するように宣言する必要があります。
しかし、そうでなく変更できない場合は、Collections.unmodifiableList(...)
でリストをラップすることができます。これにより、引数のパラメーターのスーパータイプのリストを返すことができます。 (挿入試行時にUnsupportedOperationExceptionをスローすることにより、タイプセーフの問題を回避します。)
@ericksonが説明したように、元のリストへの参照が本当に必要な場合、元の宣言でそれを再び使用したい場合は、コードがそのリストに何かを挿入しないようにしてください。それを取得する最も簡単な方法は、単純な古い非汎用リストにキャストすることです。
List<BaseClass> baseList = (List)new ArrayList<SubClass>();
リストに何が起こるかわからない場合、これをお勧めしません。リストに必要なコードを変更して、リストを受け入れるようにすることをお勧めします。
以下は便利なスニペットです。新しい配列リストを作成しますが、JVMオブジェクトのオーバーヘッドの作成は重要ではありません。
他の答えは不必要に複雑であることがわかりました。
List<BaseClass> baselist = new ArrayList<>(sublist);
List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)
あなたがやろうとしていることは非常に有用であり、私が書いているコードでは非常に頻繁にそれを行う必要があることがわかります。ユースケースの例:
インターフェイスFoo
があり、パッケージプライベート_ZorkingFoo implements Foo
_のインスタンスを作成および管理するzorking
を持つZorkingFooManager
パッケージがあるとします。 (非常に一般的なシナリオ。)
したがって、ZorkingFooManager
には_private Collection<ZorkingFoo> zorkingFoos
_を含める必要がありますが、public Collection<Foo> getAllFoos()
を公開する必要があります。
ほとんどのJavaプログラマーは、getAllFoos()
を実装する前に、新しい_ArrayList<Foo>
_を割り当て、zorkingFoos
からすべての要素を移入して返すと考えることはありません。地球上の数百万台のマシンで実行されているJavaコードによって消費されるすべてのクロックサイクルの約30%は、ガベージコレクションされたArrayListsのような不要なコピーを作成する以外は何もしません。作成。
この問題の解決策は、もちろんコレクションをダウンキャストすることです。これを行う最良の方法は次のとおりです。
_static <T,U extends T> List<T> downCastList( List<U> list )
{
return castList( list );
}
_
castList()
関数に移動します:
_static <T,E> List<T> castList( List<E> list )
{
@SuppressWarnings( "unchecked" )
List<T> result = (List<T>)list;
return result;
}
_
中間のresult
変数は、Java言語の倒錯のために必要です。
return (List<T>)list;
は、「未チェックのキャスト」例外を生成します。ここまでは順調ですね;しかしその後:
@SuppressWarnings( "unchecked" ) return (List<T>)list;
は、suppress-warningsアノテーションの不正な使用です。
したがって、return
ステートメントで_@SuppressWarnings
_を使用することは適切ではありませんが、割り当てで使用することは明らかに問題ないため、追加の「結果」変数でこの問題を解決します。 (とにかく、コンパイラーまたはJITのいずれかによって最適化されるべきです。)
これは、ジェネリックを使用して、サブクラスリストをスーパークラスにキャストする完全なコードです。
サブクラス型を渡す呼び出し元メソッド
List<SubClass> subClassParam = new ArrayList<>();
getElementDefinitionStatuses(subClassParam);
基本クラスのサブタイプを受け入れる呼び出し先メソッド
private static List<String> getElementDefinitionStatuses(List<? extends
BaseClass> baseClassVariableName) {
return allElementStatuses;
}
}
すべての要素をキャストしてください。新しいリストを作成しますが、古いリストの元のオブジェクトを参照します。
List<BaseClass> convertedList = listOfSubClass.map(x -> (BaseClass)x).collect(Collectors.toList());
このようなものも動作するはずです:
public static <T> List<T> convertListWithExtendableClasses(
final List< ? extends T> originalList,
final Class<T> clazz )
{
final List<T> newList = new ArrayList<>();
for ( final T item : originalList )
{
newList.add( item );
}// for
return newList;
}
Eclipseでclazzが必要な理由が本当にわからない。