Skip to content

List<? super List> and List<? extends List> and how to use it correctly?

Consider the following snippet:

List<Double> doubleList = null;
List<Integer> integerList = null;
List<Number> numberList = null;

//expression:1
List<? super List<? super Integer>> superDoubleList = Arrays.asList(doubleList, integerList,numberList);

//expression:2
//here doubleList will cause compilation error
List<? extends List<? super Integer>> extendsDoubleList = Arrays.asList(integerList,numberList);//doubleList
  • Here I am trying to understand how to interpret these two statements
    • Expression:1
      • Here we say that the List on the RHS must be such that all the elements of the list will satisfy the condition ? super List<? super Integer>
      • but doubleList / integerList / numberList are not satisfying this condition anyhow – as we expect a type that is a supertype of List<? super Integer>.
      • Still why are we not getting compilation error here?
    • Expression:2
      • Here we expect that the elements on the RHS must be the subtype of List<? super Integer>
      • so doubleList intuitively can be seen as a candidate that can satisfy the condition.
      • Why still I am getting compilation error if I include doubleList in the Arrays.asList expression?.

Not sure if I am interpreting the expressions in the right way – and what is wrong possibly that logically it does not seem to fit the explanation I gave above?

Answer

The two cases that compiles, compiles because the type inference algorithm tries its best to infer the type parameter for the asList call to make your code compile. It’s not about the types of the three lists (they’re only indirectly related). It’s all about the type that Arrays.asList returns.

In the first case:

List<? super List<? super Integer>> superDoubleList = Arrays.asList(doubleList, integerList,numberList);

To make your code compile, Arrays.asList, just has to create a List<List<?>>. After all, the three lists are all “lists of something“, so that is possible.

And List<List<?>> is a kind of List<? super List<? super Integer>>. This is because List<?> is a super type of List<? super Integer> – “a list of some Integer supertype” is a kind of “a list of some objects”.

Another interpretation of this, is to think of ? super T as “consumer of T” and ? extends T as “producer of T“. (PECS) In this interpretation, List<? super List<? super Integer>> means “a list that can consume lists that can consume integers”. “Consume” in the context of lists just means “add”. Can a list containing doubleList, integerList and numberList do that? Sure, it doesn’t matter what the contents of the list are, you can always add another List<? super Integer> to the list. It’s just that the type of the list has to be List<List<?>>. Even this works:

List<? super List<? super Integer>> superDoubleList =
    Arrays.asList(new ArrayList<String>(), new ArrayList<LocalDate>());

Using the same interpretation, List<? extends List<? super Integer>> means “a list that can produce lists that consume integers”. Can

Arrays.asList(integerList,numberList)

do that? Yes, both of those inner lists can consume integers, so the outer list can “produce lists that consume integers”, or in other words, a producer of such lists.

What about this list of lists?

Arrays.asList(doubleList,integerList,numberList)

Is it a producer of lists that can consume integers? Well, no, because doubleList does not consume integers, but it can produce that.

You might be wondering what is the type that the Java compiler has inferred for asList in this case:

List<? extends List<? super Integer>> extendsDoubleList = Arrays.asList(integerList,numberList);

asList could create a List<List<? super Integer>>. However, the actual inferred type seems to be something else, that can’t be expressed in Java’s syntax.