How do you cast a List of supertypes to a List of subtypes?
For example, lets say you have two classes:
public class TestA {}
public class TestB extends TestA{}
I have a method that returns a List<TestA>
and I would like to cast all the objects in that list to TestB
so that I end up with a List<TestB>
.
Solution 1:
Simply casting to List<TestB>
almost works; but it doesn't work because you can't cast a generic type of one parameter to another. However, you can cast through an intermediate wildcard type and it will be allowed (since you can cast to and from wildcard types, just with an unchecked warning):
List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;
Solution 2:
Casting of generics is not possible, but if you define the list in another way it is possible to store TestB
in it:
List<? extends TestA> myList = new ArrayList<TestA>();
You still have type checking to do when you are using the objects in the list.
Solution 3:
You really can't*:
Example is taken from this Java tutorial
Assume there are two types A
and B
such that B extends A
.
Then the following code is correct:
B b = new B();
A a = b;
The previous code is valid because B
is a subclass of A
.
Now, what happens with List<A>
and List<B>
?
It turns out that List<B>
is not a subclass of List<A>
therefore we cannot write
List<B> b = new ArrayList<>();
List<A> a = b; // error, List<B> is not of type List<A>
Furthermore, we can't even write
List<B> b = new ArrayList<>();
List<A> a = (List<A>)b; // error, List<B> is not of type List<A>
*: To make the casting possible we need a common parent for both List<A>
and List<B>
: List<?>
for example. The following is valid:
List<B> b = new ArrayList<>();
List<?> t = (List<B>)b;
List<A> a = (List<A>)t;
You will, however, get a warning. You can suppress it by adding @SuppressWarnings("unchecked")
to your method.
Solution 4:
With Java 8, you actually can
List<TestB> variable = collectionOfListA
.stream()
.map(e -> (TestB) e)
.collect(Collectors.toList());