Understanding Java Generics’ super and extends

Do you know the difference in Java generics between <? super E> and <? extends E>? If you have any doubt, read on.

In Java, classes and interfaces can have type parameters, like a List of Numbers instead of just a List:

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

With the diamond notation <>, the Java 7 compiler uses type inference to deduce the argument type so you can abbreviate instantiation to new ArrayList<>().

You can also type the variable that holds the reference to the ArrayList instance as a List, a Collection, or a Serializable.

Let’s now say you have a pair of methods that take a List of Numbers. The first method printNumbers() gets Numbers out of the List:

public void printNumbers(List<Number> list) {
    for (Number number : list) {
        System.out.print(number);
        System.out.print(", ");
    }
}

The second method, fillNumbers(), adds Numbers to the List:

public void fillNumbers(List<Number> list) {
    Number n = Integer.MAX_VALUE;
    numbers.add(n);
    numbers.add(3);
}

The method getting Numbers out of the List could just as easily work with a List of Integers or a List of Floats:

List<Number> floats = new ArrayList<>();
floats.add(Float.valueOf(1.2f));
printNumbers(floats);

What we really want though is a List, not a List:

List<Float> floats = new ArrayList<>();
floats.add(Float.valueOf(1.2f));
printNumbers(floats);

But that causes the compiler to fail with:

The method printNumbers(List) in the type Test is not applicable for the arguments (List)

To use a List, the method must be typed with List<? extends Number>.

public void printNumbers(List<? extends Number> list)

The ? extends tells the compiler that we want to use some unknown (the ?) subtype of Number. We explicitly constrain the wildcard (the ?) to represent the unknown subtype of Number. We say the method is a covariant use of the List of Numbers; it works with any List of any subtype of Number.

On the other hand, you should be able to give the method adding Numbers to the List any old List of Objects:

List<Object> objects = new ArrayList<>();
fillNumbers(objects);

That does not compile:

The method fillNumbers(List<Number>) in the type GenericsSuperExtendsDemo is not applicable for the arguments (List<Object>)

You get this to work with List<? super Number>:

public void fillNumbers(List<? super Number> list)

The ? super Number is an explicitly constrained wildcard that represents some unknown supertype of Number. We call that one a contravariant use of the List of Numbers and it works for any List of any supertype of Number.

Here is the complete example:

package test;

import java.util.ArrayList;
import java.util.List;

public class GenericsSuperExtendsDemo {

    public static void main(String[] args) {
        new GenericsSuperExtendsDemo().go();
    }

    public void go() {
        System.out.println("Hello World!");

        List<Number> numbers = new ArrayList<>();
        fillNumbers(numbers);
        printNumbers(numbers);

        {
            List<Number> floats = new ArrayList<>();
            floats.add(Float.valueOf(1.2f));
            printNumbers(floats);
        }
        {
            List<Float> floats = new ArrayList<>();
            floats.add(Float.valueOf(1.2f));
            printNumbers(floats);
        }
        {
            List<Integer> integers = new ArrayList<>();
            printNumbers(integers);
        }
        {
            List<Object> objects = new ArrayList<>();
            fillNumbers(objects);
        }
    }

    public void printNumbers(List<? extends Number> list) {
        for (Number number : list) {
            System.out.print(number);
            System.out.print(", ");
        }
    }

    public void fillNumbers(List<? super Number> list) {
        Number n = Integer.MAX_VALUE;
        list.add(n);
        list.add(3);
    }

}

The inspiration for this article is a paper presented at OOPSLA 2016 titled “Java and Scala’s Type Systems are Unsound.”

Happy Coding,
Gary Gregory

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s