Question

I'm having trouble casting a List of Fruit down to the Fruit subclass contained in the List.

public class Response {
    private List<Fruit> mFruitList;
    public List<Fruit> getFruitList() {
        return mFruitList;
    }
} 

public class Fruit {
}

public class Orange extends Fruit {
}

List<Fruit> oranges = response.getFruitList();

How do I cast oranges so that it is a List of class Orange? Is this a bad design pattern? Basically I am getting a JSON Response from a server that is a List of Fruit. For each specific call to the Web Service, I know what subclass of Fruit I will get and so I need to cast that List appropriately.

Was it helpful?

Solution

If you known for each specific call that what subclass of Fruit you will get then you should use generics instead of casting lists.

public class Response<T extends Fruit> {
    private List<T> mFruitList;
    public List<T> getFruitList() {
        return mFruitList;
    }
} 

Response<Orange> response = // create
List<Orange> oranges = response.getFruitList();

EDIT: By templates I meant generic types. Sorry, I had too much C++ nowadays

OTHER TIPS

The whole idea behind typecasts is to be able to tell the compiler, "Hey, I know more about this than you do." In your code, the compiler cannot safely downcast the List<Fruit> to List<Orange> because it can't know what the list will contain at runtime.

If you're absolutely certain that the list will be only Orange instances, and it makes your code more manageable to downcast, go for it.

List<Orange> oranges = (List<Orange>) response.getFruitList();

The compiler will give you a warning, of course, since you're doing something it thinks you shouldn't do. And just know that the JVM may have the last laugh by throwing a CastClassException if you were wrong!

Think of generics like a gate for what types of objects a list can contain. Because of this inheritance and casting won't work in the way you would expect. In the example you gave you could put both Oranges and Apples in your List<Fruit>. If the list has both apples and oranges how can you cast it to a List<Orange>.

If you need a List<Orange> then why even bother with the List<Fruit>. If you are explicitly casting it anyway and you know exactly what it contains its probably a needless abstraction.

If you are working with an API you can't change but you know exactly what it contains then you should loop through with an instanceof check just to make sure and explicity cast each Fruit instance to Orange when you need to the Orange API.

You should cast List, and test if each elements are instanceof Orange, and after the test cast in Oranges. This is the "best pratice".

Given

List<Fruit> getFruits() {...}

You can't typecast

List<Orange> oranges = (List<Orange>) getFruits();

Due to type erasure, at runtime the type of getFruits is just List. The compiler will not even let you do the downcast (I was in doubt, so I tried in Eclipse before answering).

You could tell the compiler that your list will contain some subclass of Fruit, in that case, you need to use a wildcard on you method:

List<? extends Fruit> getFruits() {...}

Then the cast becomes possible, but with a type safety warning:

@SuppressWarnings("unchecked")
List<Orange> oranges = (List<Orange>) getFruits();

Given that the runtime type of getFruits is List, you can just discard the generics type information and use an unsafe assigment:

@SuppressWarnings("unchecked")    
List<Orange> oranges = (List) getFruits();

Maybe a more elegant way as it clearly states your intention, although requiring more system resources would be:

List<Orange> oranges = Arrays.asList((Orange[])getFruits().toArray())

Arrays in Java preserve their type information at runtime, so the cast is valid and "safe" from the compiler perspective, but it can throw a runtime exception if you pass some apples in the fruit basket.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top