Most efficient way to cast List<SubClass> to List<BaseClass>
The syntax for this sort of assignment uses a wildcard:
List<SubClass> subs = ...;
List<? extends BaseClass> bases = subs;
It's important to realize that a List<SubClass>
is not interchangeable with a List<BaseClass>
. Code that retains a reference to the List<SubClass>
will expect every item in the list to be a SubClass
. If another part of code referred to the list as a List<BaseClass>
, the compiler will not complain when a BaseClass
or AnotherSubClass
is inserted. But this will cause a ClassCastException
for the first piece of code, which assumes that everything in the list is a SubClass
.
Generic collections do not behave the same as arrays in Java. Arrays are covariant; that is, it is allowed to do this:
SubClass[] subs = ...;
BaseClass[] bases = subs;
This is allowed, because the array "knows" the type of its elements. If someone attempts to store something that isn't an instance of SubClass
in the array (via the bases
reference), a runtime exception will be thrown.
Generic collections do not "know" their component type; this information is "erased" at compile time. Therefore, they can't raise a runtime exception when an invalid store occurs. Instead, a ClassCastException
will be raised at some far distant, hard-to-associate point in code when a value is read from the collection. If you heed compiler warnings about type safety, you will avoid these type errors at runtime.
erickson already explained why you can't do this, but here some solutions:
If you only want to take elements out of your base list, in principle your receiving method should be declared as taking a List<? extends BaseClass>
.
But if it isn't and you can't change it, you can wrap the list with Collections.unmodifiableList(...)
, which allows returning a List of a supertype of the argument's parameter. (It avoids the typesafety problem by throwing UnsupportedOperationException on insertion tries.)
As @erickson explained, if you really want a reference to the original list, make sure no code inserts anything to that list if you ever want to use it again under its original declaration. The simplest way to get it is to just cast it to a plain old ungeneric list:
List<BaseClass> baseList = (List)new ArrayList<SubClass>();
I would not recommend this if you don't know what happens to the List and would suggest you change whatever code needs the List to accept the List you have.