javagenericscastinginvariance

Why is it possible to cast generic class?


Java generics are invariant so it's impossible to do such cast:

List<Object> li = (List<Object>)new ArrayList<Integer>();

But in the following code in line 4 I can cast from List<Integer> to List<T>, where T could be any type. Why is that type of cast allowed?

I know it generates warning about unchecked cast, but the point is that this cast is possible inside parametrized method, but not in normal code. Keeping in mind that generics are invariant why is it allowed? In normal code when having List<Integer> I can only cast it to List<Integer> which doesn't make sense and other casts are illegal. So what is the point of allowing such cast as in line 4?

I know that generic types are removed at compile time and it ends with List xlist = (List)list, but before removing those types it is obvious that this cast should not be allowed unless it is accepted only for the case when someone passes Integer as el which doesn't make much sense.

class Test {

    public static <T> void t(List<Integer> list, T el) {
        List<T> xlist = (List<T>)list; //OK
        xlist.add(el);
    }

    public static void main(String[] args) {

        List<Integer> list = new ArrayList<>();
        t(list, "a");
        t(list, "b");

        //prints [a, b] even if List type is Integer
        System.out.println(list);

    }
}

Solution

  • In Java, it is a compile error to do an explicit cast that is known at compile time to be always incorrect or always correct. A cast from List<Integer> to List<Object> is known at compile time to be always incorrect, therefore it is not allowed. A cast from List<Integer> to List<T> is not known at compile time to be always incorrect -- it would be correct if T were Integer, and T is not known at compile time.