Bounded Wildcards

The previous tutorial covered unbounded wildcard, and now we learn to restrict the wildcards with extends (upper bound) or super (lower bound).

This tutorial uses following class hierarchy:

Java Generics Bounded Wildcard

Extends

The <? extends Type> is a wildcard with upper bound. Extends allows us to restrict the unbounded List<?> to some type and its subtypes. For example, we want restrict display method only to Pet and its subclasses,


    public void display(List<? extends Pet> list) {
        // display the list elements
    }

    display(new ArrayList<Pet>());         
    display(new ArrayList<Dog>());      
    display(new ArrayList<Bulldog>());
    display(new ArrayList<Cat>());      
    display(new ArrayList<Persian>());      

    display(new ArrayList<String>());    // error
    display(new ArrayList<Integer>());   // error         

The display(List<? extends Pet> list) accepts only the list of Pet, Dog, Bulldog, Cat, Persian and nothing else. Compiler throws error if we try to pass list of String or Integer as they are not subclasses of Pet.

We can use extends in variable declarations too as shown below:

    List<Cat> cats = new ArrayList<>();
    List<Bulldog> bulldogs = new ArrayList<>();
    List<Persian> persians = new ArrayList<>();

    List<? extends Pet> p = cats;
    List<? extends Dog> d = bulldogs;        
    List<? extends Persian> p = persians;

Various extends and allowed lists for our example class hierarchy are,

  • List<? extends Pet>   :   List<Pet>, List<Dog>, List<Cat>, List<Bulldog> or List<Persian>
  • List<? extends Dog>   :   List<Dog> or List<Bulldog>
  • List<? extends Bulldog>   :   List<Bulldog>
  • List<? extends Cat>   :   List<Cat> or List<Persian>
  • List<? extends Persian>   :   List<Persian>

To sum up, extends allows type and its subtypes.

Super

The <? super Type> is the wildcard with lower bound. Suppose, we want a parameter that allows Pet, Dog and Bulldog. As we have seen earlier, the List<? extends Pet> allows not only the list of Pet, Dog and Bulldog but also Cat and Persian, whereas the List<? extends Dog> allows only list of Dog and Bulldog but not the list of Pet. So top-down extends doesn’t met the requirement and we should use bottom-up super keyword.


    public void display(List<? super Bulldog> list) {
        // display the list elements
    }

    display(new ArrayList<Pet>());         
    display(new ArrayList<Dog>());      
    display(new ArrayList<Bulldog>());

    display(new ArrayList<Cat>());          // error
    display(new ArrayList<Persian>());      // error

The List<? super Bulldog> allows list of Bulldog and all its supertypes i.e. list of Dog and Pet which is exactly what we want.

As usual, we can use super in variable declarations as bellow:

    
    List<Pet> pets = new ArrayList<>();
    List<Cat> cats = new ArrayList<>();

    List<? super Pet> p = pets;
    List<? super Dog> d = pets;        
    List<? super Persian> p = cats;

Various super and allowed lists for our example class hierarchy are,

  • List<? super Pet>   :   List<Pet>
  • List<? super Dog>   :   List<Pet> or List<Dog>
  • List<? super Bulldog>   :   List<Pet> or List<Dog> or List<Bulldog>
  • List<? super Cat>   :   List<Pet> or List<Cat>
  • List<? super Persian>   :   List<Pet> or List<Cat> or List<Persian>

To sum up, super allows type and its super types.

Producer and Consumer

So far, we saw how wildcards sets upper or lower bounds of variables and method parameters. In addition to that, wildcards also restricts the methods can be invoked and to understand that, we categorize parameterized types based on their behavior - producers and consumers.

When we get an item from list then it acts as producer and when an item is added then it acts as consumer. Let’s see how a list changes its nature when assigned to unbounded, upper bounded or lower bounded wildcards variables.

    // acts both as producer and consumer    
    List<Dog> dogs = new ArrayList<>();         

    // now dogs act as producer 
    List<?> unbounded = dogs;                 
    
    // now dogs act as producer
    List<? extends Dogs> upperBounded = dogs; 
    
    // now dogs act both as producer and consumer
    List<? super Dogs> lowerBounded = dogs;   

The same instance of List<Dog> changes its nature when we assign it different wildcard variables. To remember this, use acronym PECS which is abbreviation for Producer extends, Consumer super.

  • List<? extends Dog> is a producer; producer extends
  • List<? super Dog> is a consumer; consumer super

Next, let’s divide methods of List into two groups - get and put methods.


public interface List<E> {

    public E get(int index);        // method without formal type parameter
    public boolean add(E element);  // method with formal type parameter

}

The methods such as E get(int index) or E remove​(int index) have no formal type parameter with E. We can ignore the return type even if it is type parameter. Similarly, boolean remove​(Object o) is also a method with no formal type parameter with E. For convenience, we call all such methods as get methods. Some more examples are:

MethodGroupReason
E get​(int index)getno E in parameter, even though return type is E
E remove​(int index)getno E in parameter, even though return type is E
boolean remove​(Object o)getno E in parameter
boolean removeAll​(Collection<?> c)getno E in parameter
boolean retainAll​(Collection<?> c)getno E in parameter

The other group consists of methods that have one or more formal type parameters. For example, the method E set​(int index, E element) and void add(E element) have formal type parameter element whose type is E. Again informally, we call all such methods as put methods. Some examples of put methods are shown below:

MethodGroupReason
void add​(int index, E element)putparameter has E (type parameter)
boolean add(​E element)putparameter has E
boolean addAll​(Collection<? extends E> c)putparameter has E
E set​(int index, E element)putparameter has E

Now, let’s know the rules about invoking methods (get and put) on plain, unbounded, and bounded types (consumers and producers).

    
    // no wildcard, acts as producer and consumer
    // can call get and put methods
    List<Dog> dogs = new ArrayList<>();    
    
    // unbound,  acts as producer
    // can call only the get methods
    List<?> unbounded = dogs;              
    
    // upper bound, acts as producer
    // can call only the get methods
    List<? extends Dog> producer = dogs;   

    // lower bound, acts as producer and consumer
    // can call get and put methods
    List<? super Dog> consumer = dogs;     

    // can call get group of methods on all the above
    dogs.get(0);
    unbounded.get(0);
    producer.get(0);
    consumer.get(0);

    // can call put group of methods only on consumers
    dogs.add(new Dog());
    unbounded.add(new Dog());    // error    
    producer.add(new Dog());     // error
    consumer.add(new Dog());    
    

All generic variables can act as producers i.e. we can call get group of methods on all of them. But List<?> and List<? extends Dogs> can’t act as consumers, that means we can’t call put group of methods (methods with one or more type parameters) on them.

Yet another way to remember is

  • can call get methods (methods without type parameter) on any type of variables.
  • can’t invoke put methods (methods with one or more type parameter) on variables with wildcard (List<?> etc.,) and wildcard with extends (List<? extends Dog> etc.,).

Final Twist

Above, we told that List<? super Dog> acts as consumer and call put methods on it. Now, consider this,

    List<Pets> pets = new ArrayList<>();          
    pets.add(new Pet());
    pets.add(new Dog());
    pets.add(new Bulldog());

    List<? super Dog> superDog = pets;
    superDog.add(new Pet());           // error
    superDog.add(new Dog());
    superDog.add(new Bulldog());
    

To List<Pet> we can add Pet, Dog or Bulldog. But, why compiler is not allowing us to add a Pet to List<? super Dog> even though it points to the same List<Pet>.

Reason being, when List<Pets> is assigned to List<? super Dog>,compiler treats it as List<Dog> which can hold only the Dog or Bulldog but not Pet.

Summary

  • List<?> accepts list of any type.
  • List<? extends T> accepts list of type and all its subtypes.
  • List<? super T> accepts list of type and all its supertypes.
  • PECS is acronym for Producer Extends Consumer Super which denotes that variable which uses extends act as producer whereas variable that use super is a consumer.
  • can call get methods (methods without type parameter) on all type of variables.
  • can’t invoke put methods (methods with one or more type parameter) on variables with wildcard (List<?> etc.,) and wildcard with super (List<? extends Dog> etc.,).
  • Compiler considers, List<Pet> assigned to List<? super Dog>, as List<Dog> and allows to add only the Dog or Bulldog, but not the Pet.

In the next tutorial we will go through wildcards in Java API to reinforce the concepts learned here.