user118967
user118967

Reputation: 5742

If B extends A, one cannot cast List<A> to List<B> (makes sense), but then why can one cast List<? extends A> to List<B>?

If a class B extends class A, one cannot cast List<A> to List<B> (cast 1 below). This makes sense, because if it were allowed, then one could read a B from the list even if it contained none.

However, in the code below, cast 2 from List<? extends A> to List<B> causes a warning. Shouldn't it generate an error for the same reason as before? In the code, the list only contains one object that is an A but not a B, and yet it is in a list deemed List<B>.

package test;

import java.util.LinkedList;
import java.util.List;

public class TypeLowerBoundCasting {

    static class A {}
    static class B extends A {}

    static void method1() {
        List<A> listOfAs = new LinkedList<A>();
        listOfAs.add(new A());
        // List<B> listOfAsAsListOfBs = (List<B>) listOfAs ; // cast 1: compiler error
        // B b = listOfAsAsListOfBs.get(0); // that would be bad
    }

    static void method2() {
        LinkedList<A> listOfAs = new LinkedList<A>();
        listOfAs.add(new A());
        List<? extends A> listOfAExtensions = listOfAs;
        List<B> listOfAsAsListOfBs = (List<B>) listOfAExtensions; // cast 2: warning, but compiles
        B b = listOfAsAsListOfBs.get(0); // that IS bad; causes a run-time error.
    }

    public static void main(String[] args) {
        method2();
    }
}

Upvotes: 0

Views: 243

Answers (1)

Jorn Vernee
Jorn Vernee

Reputation: 33895

A List<? extends A> might be a List<B>, so you can cast:

List<? extends A> list = new ArrayList<B>(); // lose type information
List<B> result = (List<B>) list; // regain type information

Similar to how you can do:

Object o = "Hello World!"; // lose type information
String s = (String) o; // regain type information

Unfortunately, the first cast is unchecked. But a cast in that place is still a valid option, as you can see.


But a List<A> can never actually be a List<B> (unless you abuse raw types), because List<A> is not assignable from a List<B> (i.e. List<B> does not 'extend' List<A>) so you can't cast:

List<A> list = new ArrayList<B>(); // Only works with: (List<A>) (List) new ArrayList<B>();

Upvotes: 3

Related Questions