Use of ‘? extends ‘ and ‘? super ‘ in Collection generics [duplicate]

The wildcards introduce restrictions in how the collection can be used.

For example, with List<? extends Number>, I can’t add new elements to the list. This is because all I know is that the list is some kind of subtype of Number, but I don’t know what that actual subtype is (so how could I know what to add?). For example, take the following code:

public void doSomethingWith(List<? extends Number> numbers) {
    numbers.add(Integer.valueOf(0)); // Won't compile
}

This won’t compile because both of these method calls are legal:

doSomethingWith(new ArrayList<Integer>());
doSomethingWith(new ArrayList<Double>());

What you can do is read elements from the list:

// This will all compile
public void doSomethingWith(List<? extends Number> numbers) {
    for (Number number : numbers) {
        // Do something with number
    }
    // OR
    Number number = numbers.get(0);
    // OR
    Number number = numbers.remove(0);
}

Calls to methods like get will return some kind of Number, we know that for a fact because of the ? extends Number, so we can treat it like that for reading purposes.

On the other hand, List<? super Integer> has exactly the opposite result. I can no longer read from the list, but I can write to it. I know that whatever ? is, it will definitely be a super-class of Integer, so concrete types of the list will definitely accept Integer values. For example:

public void doSomethingWith(List<? super Integer> integers) {
    integers.add(Integer.valueOf(0));
}

That code is completely legal. However, if you want to read from the list, the only way to do this is to use Object since anything else requires casting (which requires knowing its concrete type):

for (Object obj : integers)
// OR
Object obj = integers.get(0);
// OR
Object obj = integers.remove(0);

What’s Really Happening

Here’s what’s actually happening. When you specify ? extends Number, you’re making any method that takes elements as a parameter unusable. In fact, if you try to auto-complete code in Eclipse using Ctrl+Space on a List<? extends Number>, it shows null as the parameters’ types in the add methods and the like. Meanwhile, all the methods that return elements are guaranteed to return at least some kind of Number, though you won’t know exactly which subclass of Number it might actually be.

When you specify ? super Integer, you’re making any method that takes elements as a parameter guarantee that they’ll accept Integer values (and sub-classes of Integer as well). This allows you to call methods like add since you know they’ll accept Integer types. Meanwhile, all methods that return elements are only guaranteed to return something, but we don’t know what, so all the methods that return elements are only guaranteed to return Object.

PECS is an excellent acronym to remember this, it means “Producer Extends, Consumer Supers”. This means that if you want your list to give you something, it’s a producer, and you should use extends. If you want your list to accept things from you, it’s a consumer, so you use super. See this answer for more.

But what if I have a wildcard with no bounds?

It does both! <?> restricts you from calling methods that take the generic type as an argument and causes all the methods that return the generic type to return Object. This is because we have no idea what the type is whatsoever. For example, all of these assignments into a List<?> are legal:

List<?> list;
list = new ArrayList<Integer>();
list = new ArrayList<String>();
list = new ArrayList<MyClass>();

And so on.

Leave a Comment