web-dev-qa-db-ja.com

List <SubClass>をList <BaseClass>にキャストする最も効率的な方法

List<SubClass>として扱いたいList<BaseClass>があります。 SubClassBaseClassにキャストするのは簡単なので、問題にならないように思えますが、私のコンパイラはキャストが不可能だと文句を言います。

では、List<BaseClass>と同じオブジェクトへの参照を取得する最良の方法は何ですか?

現在、私は新しいリストを作成し、古いリストをコピーしています。

List<BaseClass> convertedList = new ArrayList<BaseClass>(listOfSubClass)

しかし、私が理解しているように、完全に新しいリストを作成する必要があります。可能であれば、元のリストへの参照をお願いします!

122
Riley Lark

この種の割り当ての構文では、ワイルドカードを使用します。

List<SubClass> subs = ...;
List<? extends BaseClass> bases = subs;

List<SubClass>notList<BaseClass>と交換可能であることを認識することが重要です。 List<SubClass>への参照を保持するコードは、リスト内のすべてのアイテムがSubClassであると想定します。コードの別の部分がList<BaseClass>としてリストを参照した場合、コンパイラはBaseClassまたはAnotherSubClassが挿入されても文句を言いません。ただし、これにより、コードの最初の部分にClassCastExceptionが発生し、リスト内のすべてがSubClassであると想定されます。

ジェネリックコレクションは、Javaの配列と同じようには動作しません。配列は共変です。つまり、次のことが許可されています。

SubClass[] subs = ...;
BaseClass[] bases = subs;

配列は要素のタイプを「知っている」ため、これは許可されます。 SubClassのインスタンスではない何かを(bases参照を介して)配列に格納しようとすると、ランタイム例外がスローされます。

ジェネリックコレクションは、notコンポーネントタイプを「認識」します。この情報はコンパイル時に「消去」されます。したがって、無効なストアが発生したときにランタイム例外を発生させることはできません。代わりに、コレクションから値が読み取られるときに、コード内のはるかに離れた、関連付けが困難なポイントでClassCastExceptionが発生します。型の安全性に関するコンパイラの警告に注意すると、実行時にこれらの型エラーを回避できます。

159
erickson

エリクソンはあなたがこれをできない理由をすでに説明しましたが、ここでいくつかの解決策があります:

基本リストから要素のみを取得する場合、原則として、受信メソッドは_List<? extends BaseClass>_を取得するように宣言する必要があります。

しかし、そうでなく変更できない場合は、Collections.unmodifiableList(...)でリストをラップすることができます。これにより、引数のパラメーターのスーパータイプのリストを返すことができます。 (挿入試行時にUnsupportedOperationExceptionをスローすることにより、タイプセーフの問題を回避します。)

37
Paŭlo Ebermann

@ericksonが説明したように、元のリストへの参照が本当に必要な場合、元の宣言でそれを再び使用したい場合は、コードがそのリストに何かを挿入しないようにしてください。それを取得する最も簡単な方法は、単純な古い非汎用リストにキャストすることです。

List<BaseClass> baseList = (List)new ArrayList<SubClass>();

リストに何が起こるかわからない場合、これをお勧めしません。リストに必要なコードを変更して、リストを受け入れるようにすることをお勧めします。

11
hd42

以下は便利なスニペットです。新しい配列リストを作成しますが、JVMオブジェクトのオーバーヘッドの作成は重要ではありません。

他の答えは不必要に複雑であることがわかりました。

List<BaseClass> baselist = new ArrayList<>(sublist);
2
sigirisetti

List<BaseClass> convertedList = Collections.checkedList(listOfSubClass, BaseClass.class)

1
jtahlborn

あなたがやろうとしていることは非常に有用であり、私が書いているコードでは非常に頻繁にそれを行う必要があることがわかります。ユースケースの例:

インターフェイス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のいずれかによって最適化されるべきです。)

1
Mike Nakis

これは、ジェネリックを使用して、サブクラスリストをスーパークラスにキャストする完全なコードです。

サブクラス型を渡す呼び出し元メソッド

List<SubClass> subClassParam = new ArrayList<>();    
getElementDefinitionStatuses(subClassParam);

基本クラスのサブタイプを受け入れる呼び出し先メソッド

private static List<String> getElementDefinitionStatuses(List<? extends 
    BaseClass> baseClassVariableName) {
     return allElementStatuses;
    }
}
0
kanaparthikiran

すべての要素をキャストしてください。新しいリストを作成しますが、古いリストの元のオブジェクトを参照します。

List<BaseClass> convertedList = listOfSubClass.map(x -> (BaseClass)x).collect(Collectors.toList());
0
drordk

このようなものも動作するはずです:

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が必要な理由が本当にわからない。

0
olivervbk