39

Suppose class B extends class A. I have a List<A> that I happen to know only contains instances of B. Is there a way I can cast the List<A> to a List<B>?

It seems my only option is to iterate over the collection, casting one element at time, creating a new collection. This seems like an utter waste of resources given type erasure makes this completely unnecessary at run-time.

1
  • 2
    Thank you everybody for the good answers. Casting to a List is the most appropriate solution in this case. I am willing to sacrifice type safety for performance in this case. Commented Oct 30, 2009 at 17:52

4 Answers 4

50

You can cast through the untyped List interface:

List<A> a = new ArrayList<A>(); List<B> b = (List)a; 
Sign up to request clarification or add additional context in comments.

6 Comments

And loose all type safety in the process! If A is a base class of B, then the list can contain A, while the code that handles b expects it to return only B objects. That's the very reason why that case is not allowed in the first place.
@Joachim, I think that is the point of the original question. The only disadvantage of this approach over the traditional cast is that if you are wrong, the bug won't appear until much later and be harder to find, whereas an iteration would reveal your mistake.
@Joachim - you are losing type-safety; the compiler cannot guarantee that the List<A> really is a List<B> which is why it won't let you do the cast in the first place. If you assert it yourself, that's fine, but this is not statically type-safe.
"I happen to know only contains instances of B". So long as he is only using the resulting list to retrieve elements and not to insert them (which could trigger a runtime check for a checkedList, for example), he should be fine.
Pavel has it. The conditions of the question was that he was already convinced it only had instances of B. Without that condition, the question doesn't even make sense.
|
18

You can try this :

List<A> a = new ArrayList<A>(); List<B> b = (List<B>) (List<?>) a; 

It is based on the answer of jarnbjo, but on don't use raw lists.

Comments

1

List<A> is not a subtype of List<B>!

The JLS even mentions that explicitly:

Subtyping does not extend through generic types: T <: U does not imply that C<T> <: C<U>.

4 Comments

IMHO: This depends. At run-time List<A> becomes List and List<B> becomes List, due to type-erasure, therefore your statement is false at run-time, as List is subtype (as in is assignable from) of List.
@pkk: that only applies if you look at the type system purely from a JVM perspective. Generics are almost entirely a language-level construct, so the JVM knows little to nothing about it (that's where I agree with you). But the whole point of generics was that they can be applied in combination with legacy code and iff the compiler gives no warning, their guarantees will hold. Casting through a raw type (i.e. doing what the JVM would do anyway) defeats the purpose of generics, by breaking the type system.
Precisely. However, I still wonder though, why on the syntax level, the following implication does not hold (i.e. is not implemented on language level, and IMO should be implemented): Foo<A> is a subtype of Foo<B> only if A is a subtype of B. That would solve the above issue (and probably other connected with generics use in Java.
@pkk: A Foo<A> could mean two things: It accepts all A or it returns only A (or both, maybe even in the same method). For "it accepts all A" (as a method parameter, for example) your suggestion would hold. But for "it returns only A" it would not: a Foo<A> would not return a B (it could, of course, but it's not guaranteed).
1

A way to retain some type safety with minimum impact on performance is to use a wrapper. This example is about Collection, the List case would be very similar and maybe one day I'll write that too. If someone else comes before me, please let's share the code.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.