With a class hierarchy below,

class Fruit {
    public void hello() {}
class Apple extends Fruit {}
class Banana extends Fruit {}

Lower Bounded Wildcards and Upper Bounded Wildcards

? super Fruit is called lower bounded wildcard.

? extends Fruit is called upper bounded wildcard.

Usually a class hierarchy is illustrated like,

   /   \
Apple Banana

The base class is placed upper, subclasses placed lower.

For ? super Fruit, the lower bound of the type argument is determined, which is Fruit, so it’s called lower bounded wildcard. Similar, for type argument ? extends Fruit, its upper bound is fixed, so it’s called upper bounded wildcard.

”? super Fruit”

void func(List<? super Fruit> fruits) {
    fruits.add(new Fruit()); // ok
    fruits.add(new Apple()); // ok
    fruits.add(new Banana()); // ok
    // Error: Cannot resolve method 'hello'

The ? in the declaration List<? super Fruit> means fruits is a List of an unknown type. The ? super Fruit means the unknown type is limited to be Fruit or its super class (Object in this case). When invoke func(List<? super Fruit> ), List<Fruit> or List<Object> can be passed as the argument.

All the fruits.add(...) in the above code is ok, because no matter what type actually the type argument is Fruit, Apple, and Banana are the subtypes of that type. An Apple object is a Fruit (or Object) object, so an Apple object can be added into a List of Fruit (or Object).

Why fruits.get(0).hello() fails?

The actual type of the elements in the fruits List is undetermined when declare the method. When call a method on an element, the compiler needs to make sure the unknown type has the method. ? super Fruit in the type argument declaration sets bounds for the unknown type, one bound (“lower”) is type Fruit, the other bound (“upper”) is type Object To safely invoke methods on the unknown type, only methods of the most upper type can be invoked. Obviously, method hello is not from the most upper type (i.e. Object). If passing a List<Object> as the parameter, then obviously the elements in fruits would not have hello() method. Therefore the compiler raises the error.

”? extends Fruit”

void func(List<? extends Fruit> fruits){
    // Error: add(capture<? extends Fruit>) in List cannot be applied to add(Fruit)
    fruits.add(new Fruit()); 
    fruits.add(new Apple()); // similar error
    fruits.add(new Banana()); // similar error
    fruits.get(0).hello(); // ok

Similar, declaring the parameter as List<? extends Fruit> means fruits is an unknown type, and can be List<Fruit>, List<Apple> or List<Banana> when the method is invoked.

Why fruits.get(0).hello() is ok in this case?

Similar, ? extends Fruit sets bounds for the unknown type, the upper bound is determined as the type Fruit. The hello() method is from the most upper type, so the compiler knows it’s safe to call it within the func body.

Why statements like fruits.add(new Fruit()) fails?

The type of fruits is unknown, it doesn’t mean the type of fruits is “dynamic”. Being “dynamic” means the type of fruits can be List<Fruit> at some point, then be List<Apple> at some other point. It’s not possible in Java, since Java is a static language. The two types, List<Fruit> and List<Apple>, have nothing to do with each other (List<Apple> is not a subtype of List<Fruit>). The type of the fruits parameter is determined on the invocation of func. For a specific invocation, the type of fruits is determined and static. For example, with an invocation like func(new ArrayList<Apple>()), the statement fruits.add(new Fruit()) would raise compiler error (since a Fruit cannot be add()ed into a List<Apple>). To ensure all the possible invocation of func works, the compiler just can’t allow the statements like fruits.add(new Fruit()) appear in the method body.

What is “capture” (capture<? extends Fruit>)? When compile the line fruits.add(new Fruit()) in IntelliJ, IntelliJ reports error message like

Error: add(capture<? extends Fruit>) in List cannot be applied to add(Fruit)

With plain javac (1.8.0_131), it reports error like

Fruit.java:8: error: no suitable method found for add(Fruit)
        fruits.add(new Fruit());
    method List.add(CAP#1) is not applicable
    (argument mismatch; Fruit cannot be converted to CAP#1)
where CAP#1 is a fresh type-variable:
    CAP#1 extends Fruit from capture of ? extends Fruit

Though the fruits is declared as an unknown type (List<? extends Fruit>), the compiler sees it as of type List<CAP#1>, where the type CAP#1 is a subclass of Fruit and is captured on an invocation of func. Clearly, a type is not assignable to its subtypes (“Fruit cannot be converted to CAP#1”), therefore the compiler raises error.

Why not just List<Fruit>

void func(List<Fruit> fruits) {
    fruits.add(new Fruit()); // ok
    fruits.add(new Apple()); // ok
    fruits.add(new Banana()); // ok
    fruits.get(0).hello(); // ok

It looks that declaring fruits as List<Fruit> fixes all the above problems. However, with func declared like this, it now accepts less types of the parameter than the two other forms above.

List<Apple> apples;
func(apples); // error, incompatible types

The type List<Apple> is not a subtype of List<Fruit>, so func(apples) raises compile error. While for func(List<? extends Fruit>), the type List<Apple> is a subtype of List<? extends Fruit>, so it can accept a parameter of type List<Apple>.

Wildcard and API Design

When design an API, usually you want to the API is as flexible as possible. Take boolean addAll(Collection<? extends E> c) in java.util.List as an example. If addAll is declared as addAll(Collection<E> c), then code below would not pass compilation.

List<Fruit> fruits = new ArrayList<>();
List<Apple> apples = ...;

PECS (Producer Extends Consumer Super)

public class Foo<T> {
    public void consume(T t) {}
    public T produce() {
        T t;
        // ...
        return t; 

Here is a generic type Foo. Foo has a method accepts a parameter of its type parameter T. For such a method, we see it as the method “consumes” objects of type T. On the other side, for methods returns an object of type T, we see it as a “produce method”.

When design methods (API) taking the generic type Foo as their arguments, we can use the PECS principle.

If in the body of a method, only “produce methods” of the generic type Foo are used, then we should declare the method’s argument as type Foo<? extends Fruit>.

void func(Foo<? extends Fruit> foo) {

If in the body of a method, only “consume methods” of Foo are used, then the method’s argument should be declared as type Foo<? super Fruit>.

void func(Foo<? super Fruit> foo) {
    Fruit fruit;
    Apple apple;

By adopting the PECS principle, the method/API allows its parameter being of more types.

An example of PECS adoption can be found in java.util.List interface.

boolean addAll(Collection<? extends E> c) is an example of “PE”. We can image that addAll is implemented as below.

boolean addAll(Collection<? extends E> c) {
    for (int i = 0; i < c.size(); ++i) {
        E e = c.get(); // get() is a "produce method"
        // add e into the list, omitted...

void sort(Comparator<? super E> c) is an example of “CS”. And assume it’s implemented as below.

void sort(Comparator<? super E> c) {
    while(not the list not fully sorted) {
        // find two elements to compare
        E e1;
        E e2;
        int result = c.compare(e1, e2); // compare is a "consume method"
        // sort depending on the result ...

List<Apple> apples;
Comparator<Fruit> comparator;

Let’s say Comparator<Fruit> sorts Fruit by weight, Comparator<Apple> sorts Apples by weight and how red an Apple is. It’s reasonable to sort List<Apple> by the common fruit comparator. The “CS” principle makes the API supports it.