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


134

私が持っているList<SubClass>私のように扱いたいということList<BaseClass>。a SubClassをaにキャストするのBaseClassは簡単なので、問題にはならないようですが、コンパイラーはキャストが不可能であると不平を言っています。

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

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

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

しかし、私が理解しているように、まったく新しいリストを作成する必要があります。可能であれば、元のリストを参照してください。


3
あなたがここにある答え:stackoverflow.com/questions/662508/...
lukastymo

回答:


175

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

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

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

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

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

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

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


Arraysでは、SubClassタイプではない(または派生した)オブジェクトを取得するときにClassCastExceptionではなく、挿入時にArrayStoreExceptionが発生することに注意してください。
アクセル

特に2つのリストを同じと見なすことができない理由の説明に感謝します。
Riley Lark、

Javaの型システムは、配列が共変であるように見せかけますが、実際には代用可能ではなく、ArrayStoreExceptionによって証明されます。
–PaŭloEbermann 2016

38

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

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

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


すごい!それを知りませんでした。
keuleJ 2014

どうもありがとうございました!
Woland 2017年

14

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

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

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


3

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

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

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

1
このコードスニペットをお寄せいただきありがとうございます。すぐに役立つ情報が得られる場合があります。適切な説明は、なぜこれが問題の優れた解決策であるを示すことによって教育的価値を大幅に改善し、同様の質問ではあるが同一ではない質問を持つ将来の読者にとってより役立つでしょう。回答を編集して説明を追加し、適用される制限と前提を示してください。特に、これは新しいリストを作成する質問のコードとどう違うのですか?
Toby Speight 2017

また、すべての参照を(浅く)コピーする必要があるため、オーバーヘッドは重要ではありません。そのため、リストのサイズに合わせてスケーリングされるため、O(n)操作です。
john16384

2

ダブルキャストを使用して、元のリストをキャストしただけの答えは見つかりませんでした。だからここでそれは完全性のためです:

List<BaseClass> baseList = (List<BaseClass>)(List<?>)subList;

何もコピーされず、操作は高速です。ただし、ここではコンパイラーをだましているのでsubList、が異なるサブタイプのアイテムを含むようにがリストを変更しないように、絶対に確認する必要があります。不変リストを扱う場合、これは通常問題ではありません。


1

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


5
このメソッドではすべての引数と結果が同じ型パラメーターを取るため、これは機能しないはずです。
パウロEbermann

奇妙な、あなたは正しいです。どういうわけか、私はこの方法が一般的なコレクションをアップキャストするのに役立つという印象を受けました。この方法でも引き続き使用でき、suppresswarningsを使用する場合は「安全な」コレクションを提供します。
jtahlborn

1

あなたがやろうとしていることは非常に有用であり、私が書くコードでは非常に頻繁に行う必要があることがわかりました。使用例:

インターフェースがFooあり、package-privateのインスタンスを作成および管理するzorkinga ZorkingFooManagerを持つパッケージがあるとしますZorkingFoo implements Foo。(非常に一般的なシナリオです。)

したがって、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アノテーションの違法な使用です。

それはコーシャ使用しない場合でもので、@SuppressWarningsreturnの文、それは余分が、この問題を解き、変数を「結果」ので、割り当てにそれを使用することが明らかに罰金です。(いずれにしても、コンパイラーまたはJITによって最適化する必要があります。)


0

このようなものもうまくいくはずです:

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

すべての要素をキャストするのはどうですか。新しいリストを作成しますが、古いリストの元のオブジェクトを参照します。

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

これは、サブクラスリストをスーパークラスにキャストするために機能する完全なコードです。
カナパルティキラン2018

0

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

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

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

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

private static List<String> getElementDefinitionStatuses(List<? extends 
    BaseClass> baseClassVariableName) {
     return allElementStatuses;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.