Wildcards

In the previous tutorial we saw that there is no relationship, what so ever, between List<Pet>, List<Dog> and List<Bulldog>, even though Pet, Dog and Bulldog belongs to same family. Each of these list is distinct and doesn’t belongs to any single group or family; moreover, we can’t assign one to another. The wildcards helps us to overcome this restriction.

In this tutorial we use following class hierarchy:

Java Generics unbounded Wildcard

Unbounded Wildcard

Consider a method to display the elements of a List.


    public void display(List<Pet> list) {
       // display the list elements       
    }
    
    display(new ArrayList<Pet>());         

    display(new ArrayList<Dog>());         // error
    display(new ArrayList<Bulldog>());     // error

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

The method can display only the list of Pet and nothing else. As explained in the previous tutorial, the parameterized type with different type arguments are not compatible with each other. This rule is also applicable to parameters of methods i.e parameter List<Pet> can accept only list of Pet and nothing else. Because of this restriction, the display method is quite useless. We can overcome this restriction using wildcard - ?.

Replace List<Pet> with List<?>


    public void display(List<?> 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>());       

The List<?> means list of unknown type. We can pass list of any type; even the List<String>, List<Integer> etc.,

It is also applicable to parametrized variables.

    
    List<Bulldog> bulldog = new ArrayList<>();
    List<Dog> dogs = bulldog;      // error    

    List<?> any = bulldog;              

Even though Bulldog is subtype of Dog, the List<Bulldog> and List<Dog> are not compatible, and compiler doesn’t allow to assign one to another. However, the variable List<?> accepts list of any type.

In essence, the wildcard ? groups the lists of any type as a single family.

Java Generics wildcard family

Wildcard Restrictions

Can we reassign List<?> any to variable of specific type? Answer is no.

    
   List<String> strings = new ArrayList<>();
   List<?> any = strings;              
      
   List<String> stringsCopy = any;  // error
   strings = any;                   // error

   stringsCopy = strings;           // allowed

Can we call any method of List<?>? No, there are some restrictions.

    
   List<Bulldog> bulldog = new ArrayList<>();
   List<?> any = bulldog;              
   
   any.add(new Bulldog());    // error
   any.add(new Dog());        // error

   Object o = any.get(1);     // allowed

Once we assign a list to List, then we can't call `add(E element)` method as compiler doesn't know the type of `List`. Only thing we can add to such list is a null.

In general, calling any method that uses type parameter triggers compilation error; The add(E e) has type parameter E and it is not allowed. However, we are free to call methods such as E get(), boolean remove​(Object o) or int size() or clear() etc., on List<?> as there is no type parameter in their parameters.

Can we call a method whose return type is type parameter? Yes, we can.

We can call method such as E get(int index) or E remove​(int index) that has type parameter in return type.

    
    List<Pet> pets = new ArrayList<>();
    pets.add(new Pet());        
        
    List<?> wildList = pets;  
    Object obj = wildList.get(0);    
    Object removedObj = wildList.remove(0);    

But, note that the type of object returned by such methods is Object as type E is unknown for List<?>.

Let’s go through some methods List<E> which are allowed on variable List<?>

    
    List<Pet> pets = new ArrayList<>();        
        
    List<?> wildList = pets;
        
    Collection<Pet> c = new ArrayList<>();
            
    // not allowed
    wildList.add(1,new Pet());         // error
    wildList.add(new Pet());           // error
    wildList.addAll(c);                // error
    wildList.set(1, new Pet());        // error
        
    // allowed    
    wildList.remove(1);
    wildList.remove(new Pet());        
    wildList.removeAll(c);
    wildList.retainAll(c);

Reason why they are allowed or not is as follows:

MethodAllowedReason
void add​(int index, E element)Noparameter has E (type parameter)
boolean add(​E element)Noparameter has E
boolean addAll​(Collection<? extends E> c)Noparameter has E
E set​(int index, E element)Noparameter has E
E remove​(int index)Yesno E in parameter, even though return type is E
boolean remove​(Object o)Yesno E in parameter
boolean removeAll​(Collection<?> c)Yesno E in parameter
boolean retainAll​(Collection<?> c)Yesno E in parameter

The above restrictions are true for all generic types that uses wildcard ?; not just the List<?>.

List<?> vs List<Object>

Remember that List<Object> and List<?> are not the same.

  • we can assign only a list of Object to List<Object>, but we can assign list of any type to List<?>
  • we can insert an Object or its subtype, into a List<Object>, but we can only insert null into a List<?>.

Summary

  • the List<?> means list of unknown type.
  • we can assign or pass list of any type to List<?>.
  • we can’t assign List<?> to any other list variable such as List<String>.
  • we can’t call methods which uses type parameter such as add(E element) on List<?>.
  • we are free to call methods such as boolean remove​(Object o) or int size() etc., on List<?> as there is no type parameter in their parameters.
  • we can also call methods such as E get(int index) or E remove​(int index) that has type parameter in return type. The object returned by such methods is of type Object.

If you feel the List<Dog> or List<String>, that can accept list of only a specific type, as one extreme; the List<?>, which accepts list of any type, is the other extreme. Can we define something in between?

Yes, Generics use extends and super keywords to fine tune the variable and parameter declarations. With these keywords we can define upper or lower bound of wildcards and next chapter explains its use.