[java] 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>.

This question is related to java list generics collections casting

The answer is


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.


You cannot cast List<TestB> to List<TestA> as Steve Kuo mentions BUT you can dump the contents of List<TestA> into List<TestB>. Try the following:

List<TestA> result = new List<TestA>();
List<TestB> data = new List<TestB>();
result.addAll(data);

I've not tried this code so there are probably mistakes but the idea is that it should iterate through the data object adding the elements (TestB objects) into the List. I hope that works for you.


This should would work

List<TestA> testAList = new ArrayList<>();
List<TestB> testBList = new ArrayList<>()
testAList.addAll(new ArrayList<>(testBList));

Quite strange that manually casting a list is still not provided by some tool box implementing something like:

@SuppressWarnings({ "unchecked", "rawtypes" })
public static <T extends E, E> List<T> cast(List<E> list) {
    return (List) list;
}

Of course, this won't check items one by one, but that is precisely what we want to avoid here, if we well know that our implementation only provides the sub-type.


The best safe way is to implement an AbstractList and cast items in implementation. I created ListUtil helper class:

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();
            }
        };
    }
}

You can use cast method to blindly cast objects in list and convert method for safe casting. Example:

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
}

The problem is that your method does NOT return a list of TestA if it contains a TestB, so what if it was correctly typed? Then this cast:

class TestA{};
class TestB extends TestA{};
List<? extends TestA> listA;
List<TestB> listB = (List<TestB>) listA;

works about as well as you could hope for (Eclipse warns you of an unchecked cast which is exactly what you are doing, so meh). So can you use this to solve your problem? Actually you can because of this:

List<TestA> badlist = null; // Actually contains TestBs, as specified
List<? extends TestA> talist = badlist;  // Umm, works
List<TextB> tblist = (List<TestB>)talist; // TADA!

Exactly what you asked for, right? or to be really exact:

List<TestB> tblist = (List<TestB>)(List<? extends TestA>) badlist;

seems to compile just fine for me.


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.


I think you are casting in the wrong direction though... if the method returns a list of TestA objects, then it really isn't safe to cast them to TestB.

Basically you are asking the compiler to let you perform TestB operations on a type TestA that does not support them.


This is possible due to type erasure. You will find that

List<TestA> x = new ArrayList<TestA>();
List<TestB> y = new ArrayList<TestB>();
x.getClass().equals(y.getClass()); // true

Internally both lists are of type List<Object>. For that reason you can't cast one to the other - there is nothing to cast.


With Java 8, you actually can

List<TestB> variable = collectionOfListA
    .stream()
    .map(e -> (TestB) e)
    .collect(Collectors.toList());

When you cast an object reference you are just casting the type of the reference, not the type of the object. casting won't change the actual type of the object.

Java doesn't have implicit rules for converting Object types. (Unlike primitives)

Instead you need to provide how to convert one type to another and call it manually.

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));
}

This is more verbose than in a language with direct support, but it works and you shouldn't need to do this very often.


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);
}

This test shows how to cast List<Object> to List<MyClass>. But you need to take an attention to that objectList must contain instances of the same type as MyClass. And this example can be considered when List<T> is used. For this purpose get field Class<T> clazz in constructor and use it instead of MyClass.class.


You can use the selectInstances method in Eclipse Collections. This will involved creating a new collection however so will not be as efficient as the accepted solution which uses casting.

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);

I included StringBuffer in the example to show that selectInstances not only downcasts the type, but will also filter if the collection contains mixed types.

Note: I am a committer for Eclipse Collections.


Since this is a widely referenced question, and the current answers mainly explain why it does not work (or propose hacky, dangerous solutions that I would never ever like to see in production code), I think it is appropriate to add another answer, showing the pitfalls, and a possible solution.


The reason why this does not work in general has already been pointed out in other answers: Whether or not the conversion is actually valid depends on the types of the objects that are contained in the original list. When there are objects in the list whose type is not of type TestB, but of a different subclass of TestA, then the cast is not valid.


Of course, the casts may be valid. You sometimes have information about the types that is not available for the compiler. In these cases, it is possible to cast the lists, but in general, it is not recommended:

One could either...

  • ... cast the whole list or
  • ... cast all elements of the list

The implications of the first approach (which corresponds to the currently accepted answer) are subtle. It might seem to work properly at the first glance. But if there are wrong types in the input list, then a ClassCastException will be thrown, maybe at a completely different location in the code, and it may be hard to debug this and to find out where the wrong element slipped into the list. The worst problem is that someone might even add the invalid elements after the list has been casted, making debugging even more difficult.

The problem of debugging these spurious ClassCastExceptions can be alleviated with the Collections#checkedCollection family of methods.


Filtering the list based on the type

A more type-safe way of converting from a List<Supertype> to a List<Subtype> is to actually filter the list, and create a new list that contains only elements that have certain type. There are some degrees of freedom for the implementation of such a method (e.g. regarding the treatment of null entries), but one possible implementation may look like this:

/**
 * 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;
}

This method can be used in order to filter arbitrary lists (not only with a given Subtype-Supertype relationship regarding the type parameters), as in this example:

// 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]

The only way I know is by copying:

List<TestB> list = new ArrayList<TestB> (
    Arrays.asList (
        testAList.toArray(new TestB[0])
    )
);

if you have an object of the class TestA, you can't cast it to TestB. every TestB is a TestA, but not the other way.

in the following code:

TestA a = new TestA();
TestB b = (TestB) a;

the second line would throw a ClassCastException.

you can only cast a TestA reference if the object itself is TestB. for example:

TestA a = new TestB();
TestB b = (TestB) a;

so, you may not always cast a list of TestA to a list of TestB.


Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to list

Convert List to Pandas Dataframe Column Python find elements in one list that are not in the other Sorting a list with stream.sorted() in Java Python Loop: List Index Out of Range How to combine two lists in R How do I multiply each element in a list by a number? Save a list to a .txt file The most efficient way to remove first N elements in a list? TypeError: list indices must be integers or slices, not str Parse JSON String into List<string>

Examples related to generics

Instantiating a generic type Are these methods thread safe? The given key was not present in the dictionary. Which key? Using Java generics for JPA findAll() query with WHERE clause Using Spring RestTemplate in generic method with generic parameter How to create a generic array? Create a List of primitive int? How to have Java method return generic list of any type? Create a new object from type parameter in generic class What is the "proper" way to cast Hibernate Query.list() to List<Type>?

Examples related to collections

Kotlin's List missing "add", "remove", Map missing "put", etc? How to unset (remove) a collection element after fetching it? How can I get a List from some class properties with Java 8 Stream? Java 8 stream map to list of keys sorted by values How to convert String into Hashmap in java How can I turn a List of Lists into a List in Java 8? MongoDB Show all contents from all collections Get nth character of a string in Swift programming language Java 8 Distinct by property Is there a typescript List<> and/or Map<> class/library?

Examples related to casting

Subtracting 1 day from a timestamp date Cast object to interface in TypeScript TypeScript enum to object array Casting a number to a string in TypeScript Hive cast string to date dd-MM-yyyy Casting int to bool in C/C++ Swift double to string No function matches the given name and argument types C convert floating point to int PostgreSQL : cast string to date DD/MM/YYYY