1

Assigning a List<Object> to a List<? super String> works fine.

Assigning a List<List<Object>> to a List<List<? super String>> doesn't compile.

Code

public class Main {
    public static void main(String[] args) {
        // works fine
        List<Object> listOfObject = new ArrayList<>();
        takeListSuperString(listOfObject);

        // doesn't compile
        List<List<String>> listOfListOfObject = new ArrayList<>();
        takeListOfListSuperString(listOfListOfObject);
    }

    static void takeListSuperString(List<? super String> listSuperString) {

    }

    static void takeListOfListSuperString(List<List<? super String>> listOfListSuperString) {

    }
}

Question

Why doesn't List<List<? super String>> work the same as List<? super String>?

Also, any idea where I could look up things like this?

A related question is Generics hell: hamcrest matcher as a method parameter. But I don't find the answers there helpful.

Edit

I had to think through JB Nizet's answer for a couple of hours before I finally got it. So I'll expand it a little bit here. Maybe that'll help someone else.

Assuming that assigning a List<List<CharSequence>> to a List<List<? super String>> is possible, the following code would compile:

// can only contain instances of CharSequence
List<List<CharSequence>> listOfListOfCharSequences = new ArrayList<>();

List<List<? super String>> listOfListSuperString = listOfListOfCharSequences;

// add a list of objects containing an Integer
List<Object> listOfObjects = new ArrayList<>();
listOfObjects.add(123);
listOfListSuperString.add(listOfObjects);

// Ups.. exception at runtime we are getting an Integer where we expect a CharSequence
CharSequence charSequence = listOfListOfCharSequences.get(0).get(0);

So to prevent ugly exceptions at runtime it is not allowed.

As halex points out this is Generics covariance, same as a List<String> not being assignable to List<Object>. And with using List<? extends List<? super String>> the code would actually compile, because ? extends String prevents the List.add() call.

Community
  • 1
  • 1
Arend v. Reinersdorff
  • 4,110
  • 2
  • 36
  • 40

3 Answers3

7

Because a List<Object> is not the same thing as a List<? super String>. A List<? super String> holds objects of one specific type, that you don't know, but which is String or a super-class or super-interface of String. Whereas a List<Object> is a List that can hold any kind of object.

Suppose ? super String is CharSequence. Would you find it normal that the following compiles?

List<List<Object>> listOfListOfObjects = new ArrayList<List<Object>>();
List<Object> listOfObjects = new ArrayList<Object>(); 
listOfObjects.add(new Integer());
listOfListOfObjects.add(listOfObjects);
List<List<CharSequence>> listOfListOfCharSequences = listOfListOfObjects; // WTF?
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
4

You have to change the method takeListOfListSuperString to

static void takeListOfListSuperString(List<? extends List<? super String>> ListOfListSuperString)

The reason for this is covariance. The inner type List<? super String> must be matched exactly and that is not the case for List<String>. The workaround is to use another wildcard that tells Java that the combined inner type should also be covariant.

halex
  • 16,253
  • 5
  • 58
  • 67
  • `? extends List` works. Cool. I still don't understand why the "inner inner type" in `List>` has to match exactly but the inner type in `List super String>` does not. – Arend v. Reinersdorff Nov 09 '12 at 08:35
0

Okay question asked above is different... We can understand the question as follows:

List<? super String> a = new ArrayList<Object>();
List<List<? super String>> b = new ArrayList<List<Object>>();

List<List<? super String>> c = new ArrayList<List<? super String>>();

In this code line 2 is not compiling.... So why is not compiling?

Answer:

List<List<? super String>> means list of lists which are super class of String i.e. CharSequence or Object

But the object we are assigning says ArrayList of Lists which can accept any any class and every class.

So both the lists are different, and java shows this different at the time of compilations.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Vishal
  • 3,189
  • 1
  • 15
  • 18