How do you cast a List of supertypes to a List of subtypes?

前端 未结 17 1519
面向向阳花
面向向阳花 2020-11-22 08:43

For example, lets say you have two classes:

public class TestA {}
public class TestB extends TestA{}

I have a method that returns a L

17条回答
  •  温柔的废话
    2020-11-22 09:11

    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 to a List 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  List filter(List listA, Class c)
    {
        List listB = new ArrayList();
        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" that actually 
    // contains Integer, Double and Float values
    List mixedNumbers = 
        new ArrayList(Arrays.asList(12, 3.4, 5.6f, 78));
    
    // Filter the list, and create a list that contains
    // only the Integer values:
    List integers = filter(mixedNumbers, Integer.class);
    
    System.out.println(integers); // Prints [12, 78]
    

提交回复
热议问题