Reputation: 11953
I learned that if I have two classes:
public class A {}
public class B extends A {}
I can cast from a List<A>
to List<B>
by doing this:
List<B> list = (List<B>)(List<?>) collectionOfListA;
This would generate a warning, but it works. However, I read that this is not recommended. Is it true in that case?
If I know it's returning me a List<B>
, why making this cast is such a bad practice?
Please note that I want to know why, in this case, this is a bad practice, not why there is a warning. And "it's a bad practice because it generates a warning" is not a good answer.
Upvotes: 5
Views: 414
Reputation: 29223
For this to make sense, you have to understand that List<A>
and List<B>
are two different types, not hierarchically related, even if A
and B
are hierarchically related. Even if B
was a subclass of A
, casting a List<B>
to a List<A>
is bad because it could allow you to add to that list instances of A
(but not B
) and the compiler would happily do that, even though it wouldn't agree with the actual (runtime) type of the collection.
I didn't know that you can circumvent this in Java, but if you can it's only because of type erasure: because Java creates one class such as List<Object>
and, for different "implementations" of that generic class, it merely adds casts in your code before compiling it. This is contrast to (say) C#, which actually uses the List<T>
only as a "compiled" template (called "generic definition"), to create the concrete types on demand.
Upvotes: 2
Reputation: 911
Casting the list directly is bad because it subverts the Java type system. The cast will always succeed, but the program may fail later with a ClassCastException
when you go to retrieve items, at which point the source of the error may be unclear.
In general you want your program to fail as close to the source of the error as possible. If you cast the list, it may be passed around for a while before someone actually tries to access elements and by the time it throws a ClassCastException
it might be really hard to track it back to that cast (at the very least hard-er than if the program failed at the cast).
From your comments it seems like you're sure that everything in listA
is actually a B
in which case I would recommend:
List<B> listB = new ArrayList(listA.size());
for (A a : listA) {
if (a == null || a instanceof B) {
listB.add((B) a);
} else {
//Either ignore or throw exception
}
}
Upvotes: 3
Reputation: 10677
Generics work bit differently, so list< B > is not of type list< A > though B is a type of A.
Upvotes: 0
Reputation: 272347
An apple is a fruit, but a list of apples is-not a list of fruit.
If it was, you could put a banana in a list of apples
Upvotes: 13