たとえば、次の2つのクラスがあるとします。
public class TestA {}
public class TestB extends TestA{}
私はを返すメソッドがありList<TestA>
、そのリスト内のすべてのオブジェクトをにキャストして、でTestB
終わるようにしたいと思いますList<TestB>
。
たとえば、次の2つのクラスがあるとします。
public class TestA {}
public class TestB extends TestA{}
私はを返すメソッドがありList<TestA>
、そのリスト内のすべてのオブジェクトをにキャストして、でTestB
終わるようにしたいと思いますList<TestB>
。
回答:
List<TestB>
ほぼ機能するようにキャストするだけです。ただし、1つのパラメーターのジェネリック型を別のパラメーターにキャストできないため、機能しません。ただし、中間のワイルドカードタイプを使用してキャストでき、許可されます(ワイルドカードタイプにキャストしたり、ワイルドカードタイプからキャストしたりできるため、チェックされていない警告が表示されます)。
List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;
ジェネリックのキャストは不可能ですが、別の方法でリストを定義すると、TestBをそこに格納できます。
List<? extends TestA> myList = new ArrayList<TestA>();
リスト内のオブジェクトを使用している場合でも、型チェックを行う必要があります。
Cannot instantiate the type ArrayList<? extends TestA>
。
あなたは本当にできない*:
このJavaチュートリアルからの例
2種類があると仮定A
してB
、その結果B extends A
。その後、次のコードは正しいです:
B b = new B();
A a = b;
B
はのサブクラスであるため、前のコードは有効ですA
。では、List<A>
and で何が起こりList<B>
ますか?
それはのList<B>
サブクラスではないことが判明したList<A>
ので、私たちは書くことができません
List<B> b = new ArrayList<>();
List<A> a = b; // error, List<B> is not of type List<A>
さらに、私たちは書くことさえできません
List<B> b = new ArrayList<>();
List<A> a = (List<A>)b; // error, List<B> is not of type List<A>
*:キャスト可能性を作るために、我々は両方のための共通の親を必要List<A>
とList<B>
:List<?>
例えば。以下が有効です。
List<B> b = new ArrayList<>();
List<?> t = (List<B>)b;
List<A> a = (List<A>)t;
ただし、警告が表示されます。@SuppressWarnings("unchecked")
メソッドに追加することで抑制できます。
Java 8では、実際にできること
List<TestB> variable = collectionOfListA
.stream()
.map(e -> (TestB) e)
.collect(Collectors.toList());
new List<TestB>
か?
これは広く参照されている質問であり、現在の回答は主になぜそれが機能しないのかを説明している(または私が製品コードで見たくないハックで危険な解決策を提案している)ので、別の回答を追加して、落とし穴、および可能な解決策。
これが一般に機能しない理由は、他の回答ですでに指摘されています。変換が実際に有効かどうかは、元のリストに含まれているオブジェクトのタイプによって異なります。そこタイプではないタイプのものであり、リスト内のオブジェクトである場合はTestB
、しかし、別のサブクラスでTestA
は、キャストがあるではない有効な。
もちろん、キャストは有効です。コンパイラーで使用できないタイプに関する情報がある場合があります。これらの場合、リストをキャストすることは可能ですが、一般的には推奨されません。
いずれか...
(現在受け入れられている回答に対応する)最初のアプローチの意味は微妙です。一見すると正常に動作しているように見えるかもしれません。しかし、入力リストに間違ったタイプがある場合ClassCastException
、おそらくコードの完全に異なる場所にスローされ、これをデバッグして、間違った要素がリストにどこに落ちたのかを見つけるのは難しいかもしれません。最悪の問題は、リストがキャストされた後に誰かが無効な要素を追加して、デバッグをさらに困難にする可能性があることです。
これらのスプリアスをデバッグする問題は、一連のメソッドでClassCastExceptions
軽減できますCollections#checkedCollection
。
aからa List<Supertype>
に変換するよりタイプセーフな方法List<Subtype>
は、実際にリストをフィルタリングし、特定のタイプの要素のみを含む新しいリストを作成することです。このようなメソッドの実装にはある程度の自由度があります(たとえば、null
エントリの処理に関して)が、1つの可能な実装は次のようになります。
/**
* Filter the given list, and create a new list that only contains
* the elements that are (subtypes) of the class c
*
* @param listA The input list
* @param c The class to filter for
* @return The filtered list
*/
private static <T> List<T> filter(List<?> listA, Class<T> c)
{
List<T> listB = new ArrayList<T>();
for (Object a : listA)
{
if (c.isInstance(a))
{
listB.add(c.cast(a));
}
}
return listB;
}
このメソッドは、次の例のように、任意のリストをフィルタリングするために使用できます(型パラメーターに関する特定のサブタイプとスーパータイプの関係だけでなく)。
// A list of type "List<Number>" that actually
// contains Integer, Double and Float values
List<Number> mixedNumbers =
new ArrayList<Number>(Arrays.asList(12, 3.4, 5.6f, 78));
// Filter the list, and create a list that contains
// only the Integer values:
List<Integer> integers = filter(mixedNumbers, Integer.class);
System.out.println(integers); // Prints [12, 78]
Steve Kuoが言及しているようににキャストList<TestB>
することはできませList<TestA>
んが、の内容をList<TestA>
にダンプできますList<TestB>
。以下を試してください:
List<TestA> result = new List<TestA>();
List<TestB> data = new List<TestB>();
result.addAll(data);
私はこのコードを試していないので、おそらく間違いがありますが、アイデアは、データオブジェクトを繰り返し処理して、要素(TestBオブジェクト)をリストに追加する必要があるというものです。それがうまくいくことを願っています。
最良の安全な方法は、実装でAbstractList
項目をキャストして項目をキャストすることです。私はListUtil
ヘルパークラスを作成しました:
public class ListUtil
{
public static <TCastTo, TCastFrom extends TCastTo> List<TCastTo> convert(final List<TCastFrom> list)
{
return new AbstractList<TCastTo>() {
@Override
public TCastTo get(int i)
{
return list.get(i);
}
@Override
public int size()
{
return list.size();
}
};
}
public static <TCastTo, TCastFrom> List<TCastTo> cast(final List<TCastFrom> list)
{
return new AbstractList<TCastTo>() {
@Override
public TCastTo get(int i)
{
return (TCastTo)list.get(i);
}
@Override
public int size()
{
return list.size();
}
};
}
}
cast
methodを使用して、リスト内のオブジェクトを盲目的にキャストし、convert
メソッドを安全にキャストできます。例:
void test(List<TestA> listA, List<TestB> listB)
{
List<TestB> castedB = ListUtil.cast(listA); // all items are blindly casted
List<TestB> convertedB = ListUtil.<TestB, TestA>convert(listA); // wrong cause TestA does not extend TestB
List<TestA> convertedA = ListUtil.<TestA, TestB>convert(listB); // OK all items are safely casted
}
私が知っている唯一の方法はコピーすることです:
List<TestB> list = new ArrayList<TestB> (
Arrays.asList (
testAList.toArray(new TestB[0])
)
);
オブジェクト参照をキャストする場合は、オブジェクトのタイプではなく、参照のタイプをキャストするだけです。キャストしてもオブジェクトの実際のタイプは変更されません。
Javaには、オブジェクト型を変換するための暗黙のルールはありません。(プリミティブとは異なります)
代わりに、あるタイプを別のタイプに変換して手動で呼び出す方法を提供する必要があります。
public class TestA {}
public class TestB extends TestA{
TestB(TestA testA) {
// build a TestB from a TestA
}
}
List<TestA> result = ....
List<TestB> data = new List<TestB>();
for(TestA testA : result) {
data.add(new TestB(testA));
}
これは、直接サポートされている言語よりも詳細ですが、機能するため、頻繁に実行する必要はありません。
クラスのオブジェクトがある場合、TestA
それをにキャストすることはできませんTestB
。すべてTestB
がですTestA
が、逆ではありません。
次のコードで:
TestA a = new TestA();
TestB b = (TestB) a;
2行目はをスローしClassCastException
ます。
TestA
オブジェクト自体がの場合にのみ、参照をキャストできますTestB
。例えば:
TestA a = new TestB();
TestB b = (TestB) a;
そう、あなたはいつものリストを唱えないことTestA
のリストにTestB
。
このselectInstances
方法は、Eclipseコレクションで使用できます。これには新しいコレクションの作成が含まれますが、キャストを使用する承認済みのソリューションほど効率的ではありません。
List<CharSequence> parent =
Arrays.asList("1","2","3", new StringBuffer("4"));
List<String> strings =
Lists.adapt(parent).selectInstancesOf(String.class);
Assert.assertEquals(Arrays.asList("1","2","3"), strings);
StringBuffer
例に含めたのselectInstances
は、型をダウンキャストするだけでなく、コレクションに混合型が含まれている場合にもフィルタリングすることを示しています。
注:私はEclipseコレクションのコミッターです。
これは型消去のために可能です。あなたはそれを見つけるでしょう
List<TestA> x = new ArrayList<TestA>();
List<TestB> y = new ArrayList<TestB>();
x.getClass().equals(y.getClass()); // true
内部的には両方のリストのタイプList<Object>
です。そのため、一方を他方にキャストすることはできません。キャストするものはありません。
Integer j = 42; Integer i = (Integer) j;
正常に動作します。どちらも整数であり、どちらも同じクラスを持っているため、「キャストするものは何もありません」。
問題は、メソッドにTestBが含まれている場合、メソッドがTestAのリストを返さないことです。そのため、正しく入力された場合はどうなりますか?次に、このキャスト:
class TestA{};
class TestB extends TestA{};
List<? extends TestA> listA;
List<TestB> listB = (List<TestB>) listA;
期待どおりに動作します(Eclipseは、あなたがやっていることとまったく同じであるチェックされていないキャストについて警告します)。これを使用して問題を解決できますか?実際には、これが原因で可能です:
List<TestA> badlist = null; // Actually contains TestBs, as specified
List<? extends TestA> talist = badlist; // Umm, works
List<TextB> tblist = (List<TestB>)talist; // TADA!
まさにあなたが求めたものですよね?または本当に正確に言うと:
List<TestB> tblist = (List<TestB>)(List<? extends TestA>) badlist;
私にとってはうまくコンパイルできるようです。
class MyClass {
String field;
MyClass(String field) {
this.field = field;
}
}
@Test
public void testTypeCast() {
List<Object> objectList = Arrays.asList(new MyClass("1"), new MyClass("2"));
Class<MyClass> clazz = MyClass.class;
List<MyClass> myClassList = objectList.stream()
.map(clazz::cast)
.collect(Collectors.toList());
assertEquals(objectList.size(), myClassList.size());
assertEquals(objectList, myClassList);
}
このテストは、にキャストList<Object>
する方法を示していますList<MyClass>
。ただしobjectList
、と同じタイプのインスタンスが含まれている必要があることに注意する必要がありますMyClass
。また、この例は、List<T>
を使用する場合に考慮できます。この目的のためにClass<T> clazz
、コンストラクタでフィールドを取得し、代わりにそれを使用しますMyClass.class
。