bladekp bladekp - 2 months ago 8
Java Question

Java generic methods, wildcard List return type

Java's 8 method signature looks as follows:

public static <E extends CharSequence> List<? super E> m(List<E> list);


And somewhere in code, method call:

result = m(list);


Suppose that I've create argument
list
as follows:

List<String> list = new ArrayList<>();


My question is, why the
result
can be just raw
List
, and can't be even a
List<Object>
?

Answer

Calling your method with a List<String> as an argument is like calling the following method:

public static List<? super String> m(List<String> list) { ... }

Consider the following starting code:

List<String> list = new ArrayList<>();
List<? super String> result = m(list);

This calls the above mentioned method and stores the return value into the variable result - which is typed exactly with the method's return type.

The question now is: To what variables - or better what types - can you assign this variable? So we are speaking about assignment compatibility.

Consider these assignments:

List<String> result1 = result; // compiler error: type mismatch (not assignable)
List<Object> result2 = result; // compiler error: type mismatch (not assignable)

List result3 = result; // ok
List<?> result4 = result; // ok

List<? super String> result5 = result; // ok
List<? extends Object> result6 = result; // ok

To understand the nature of this error, you must know that generics are invariant. That means, the type List<String> is not a subtype of List<Object> - although the types String and Object have such a subtype hierarchy.

So, what we are trying here is:

  1. Assign a List<? super String> to a List<String> => fails, no subtyping
  2. Assign a List<? super String> to a List<Object> => fails, no subtyping
  3. Assign a List<? super String> to a List => succeeds, because using raw types opts out from type checking generally
  4. Assign a List<? super String> to a List<?> => succeeds, because a List<?> is the supertype of all List<...>
  5. Assign a List<? super String> to a List<? super String> => succeeds, because ... well ...
  6. Assign a List<? super String> to a List<? ectends Object> => succeeds, because List<? ectends Object> is essentially the same as List<?>.

Note, that trying to assign a List<? super String> to a List<? super CharSequence> or a List<? extends CharSequence> will also fail. They are not in a subtype hierarchy.

Why is that so? The compiler cannot guarantee that the actual list was instantiated with a type that matches the constraint ? super/extends CharSequence.