Java Generics: Cannot Cast List<Subclass> to List<Superclass>

Java Generics: Cannot cast ListSubClass to ListSuperClass?

What you're seeing in the second case is array covariance. It's a bad thing IMO, which makes assignments within the array unsafe - they can fail at execution time, despite being fine at compile time.

In the first case, imagine that the code did compile, and was followed by:

b1.add(new SomeOtherTree());
DataNode node = a1.get(0);

What would you expect to happen?

You can do this:

List<DataNode> a1 = new ArrayList<DataNode>();
List<? extends Tree> b1 = a1;

... because then you can only fetch things from b1, and they're guaranteed to be compatible with Tree. You can't call b1.add(...) precisely because the compiler won't know whether it's safe or not.

Have a look at this section of Angelika Langer's Java Generics FAQ for more information.

Most efficient way to cast ListSubClass to ListBaseClass

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.

Java Generics -- Assigning a list of subclass to a list of superclass

To explain this, let me substitute "B" with Integer and "A" with Number. This is just to make it a little easier to explain.

Class Integer extends Number;

List <Integer> iList = new ArrayList<Integer>();
List <Number> nList = iList // will fail

The reason this would fail is because nList can take any Number -- it can take Integer, it can take Double, or for that matter any subclass of Number. However, this is not true for iList. You cannot add a Double to iList because it accepts only Integer and its subclasses. Hope this helps explain it to you.

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

Simply casting to List<TestB> almost works; but it doesn't work because you can't cast a generic type of one parameter to another. However, you can cast through an intermediate wildcard type and it will be allowed (since you can cast to and from wildcard types, just with an unchecked warning):

List<TestB> variable = (List<TestB>)(List<?>) collectionOfListA;

Cast the sub class List to base class List

No it will not work directly except you define your first list as:

List<? extends Animals> animal;

then you will be able to do:

List<Puppy> puppy = new ArrayList<Puppy>();
animal = puppy;

Java Sub Class List Can NOT be Returned As Super Class List

You cannot explicitly cast from List< A > to List< B >. You must iterate and cast each element individually.

public List<B> castAList(List<A> aList){

List<B> bList = new ArrayList<>();

for(A a : aList){
bList.add((B) a);
}

return bList;
}

Another solution is to explicitly declare the inheritance within the generics by taking advantage of the wildcard ?

List<? extends SuperClass> superList

Which part of the JLS specifies that you can't cast from List? extends ListSuperclass to ListListSubclass?

Well, I'll just say it after Holger's confirmation and answer: JLS is underspecified (at least) at this location. There are some related JDK bugs that work around the same idea, noticeable this one, that directly hits your question via:

.... Otherwise, map wildcards and type variables to their upper bounds, and then test whether their erasures are related classes or interfaces (that is, one erased type is a subtype of the other)

Only to immediately start the next sentence with:

This is unsound...

So, that bug admits that JLS needs some corrections around this chapter(s).

What I have been struggling with too from your quotes of the JLS are two points:

  • One type argument is a type variable or wildcard, with an upper bound (from capture conversion (§5.1.10), if necessary)...

    I do know what capture conversion is, but I was not aware it might be optional to be performed (via that "if necessary"). I always thought that it is performed at each location, all the time.

  • What is an upper bound from a captured conversion type?

    In your case, is that upper bound List<Number> or List<?>, for example? In my understanding (or lack of precise JLS explanation), this could be understood either way.

All these (+ your great scrape of the JLS) makes me wonder about the correctness of the JLS here, especially since javac is not following these exact same rules.

Is ListDog a subclass of ListAnimal? Why are Java generics not implicitly polymorphic?

No, a List<Dog> is not a List<Animal>. Consider what you can do with a List<Animal> - you can add any animal to it... including a cat. Now, can you logically add a cat to a litter of puppies? Absolutely not.

// Illegal code - because otherwise life would be Bad
List<Dog> dogs = new ArrayList<Dog>(); // ArrayList implements List
List<Animal> animals = dogs; // Awooga awooga
animals.add(new Cat());
Dog dog = dogs.get(0); // This should be safe, right?

Suddenly you have a very confused cat.

Now, you can't add a Cat to a List<? extends Animal> because you don't know it's a List<Cat>. You can retrieve a value and know that it will be an Animal, but you can't add arbitrary animals. The reverse is true for List<? super Animal> - in that case you can add an Animal to it safely, but you don't know anything about what might be retrieved from it, because it could be a List<Object>.

Cannot convert from not generic subclass to generic superclass

This more concrete example illustrates your problem which is one of type parameter variance.

void foo(List<String> stringList, Integer anInteger) {
List<Object> objList = (List<Object>) stringList;
objList.add(anInteger); // Violation -- adding an object to a list of strings
// could cause someone getting a "String" to get an
// Integer stead
}

so a List<String> is not a List<Object> although it is a List<? extends Object>.


In your specific instance you can't cast

PlaceDataSource to IDictionaryDataSource<T>

PlaceDataSource is an IDictionaryDataSource<Place>, but the only thing we know about <T> is that it extends BaseDictionary which is a super-class of BaseDictionary.

So you can cast a PlaceDataSource to

  1. an IDictionaryDataSource<Place> or to
  2. an IDictionaryDataSource<? super Place> or to
  3. an IDictionaryDataSource<? extends BaseDictionary>

but not to an IDictionaryDataSource<T> because T is not guaranteed to be Place, and doing so would lead to a mismatch between the actual type parameter Place and the formal type parameter T.



Related Topics



Leave a reply



Submit