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>
orList<?>
, 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
toIDictionaryDataSource<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
- an
IDictionaryDataSource<Place>
or to - an
IDictionaryDataSource<? super Place>
or to - 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
Synchronizing on an Integer Value
Windows Shortcut (.Lnk) Parser in Java
Reverse Each Individual Word of "Hello World" String with Java
Can the Jvm Recover from an Outofmemoryerror Without a Restart
How to Add Unicode in Truetype0Font on PDFbox 2.0.0
Package Conflicts with Automatic Modules in Java 9
How to Keep a Scanner from Throwing Exceptions When the Wrong Type Is Entered
Java 1.6: Creating an Array of List<T>
Docker: Combine Multiple Images
How to Implement Dynamic Gui in Swing
How to Add Checkboxes to Jtable Swing
Varying Behavior for Possible Loss of Precision
Java Socket/Serialization, Object Won't Update
Differencebetween @Inject and @Autowired in Spring Framework? Which One to Use Under What Condition
Run a Single Test Method with Maven
Jpa: How to Have One-To-Many Relation of the Same Entity Type
How to Create 2 Separate Log Files with One Log4J Config File