Wildcards

The previous tutorial explained that there is no relationship, what so ever, between List<Food>, List<Snack> and List<Cookie>,even though Food, Snack and Cookie 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.

Unbounded Wildcard

Consider a method to display the elements of a List.

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

    display(new ArrayList<Snack>());         // error
    display(new ArrayList<Cookie>());        // error

    display(new ArrayList<Fruit>());         // error 
    display(new ArrayList<Apple>());         // error

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

The method can display only the list of Food and nothing else. As explained in the previous tutorial, the variable of generic type can’t be assigned to another. This rule is also applicable to parameters of methods i.e parameter List<Food> can accept only list of Food and nothing else. Because of this restriction, the display method is quite useless. We can overcome this restriction using wildcard - ?. Replace List<Food> with List<?>

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

    display(new ArrayList<Food>());         
    display(new ArrayList<Snack>());        
    display(new ArrayList<Cookie>());
    display(new ArrayList<Fruit>());        
    display(new ArrayList<Apple>()); 
    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.,

We can do the same thing with variables.

    
    List<Cookie> cookies = new ArrayList<>();
    List<Snack> snacks = cookies;              // error    

    List<?> any = cookies;              

Even though Cookie is subtype of Snack, there is no relationship between List<Cookie> and List<Snack> and compiler doesn’t allow to assign one to another. But the variable List<?> accepts list of any type.

In essence, the wildcard ? clubs the lists of any type into a single group or 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 on List<?>? No, there are some restrictions.

    
   List<Cookie> cookies = new ArrayList<>();
   List<?> any = cookies;              
   
   any.add(new Cookie());  // error
   any.add(new Snack());   // 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.

However, we are free to call methods such as 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<Food> foods = new ArrayList<>();
    foods.add(new Food());        
        
    List<?> wildList = foods;  
    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<Food> foods = new ArrayList<>();        
        
    List<?> wildList = foods;
        
    Collection<Food> c = new ArrayList<>();
            
    // not allowed
    wildList.add(1,new Food());         // error
    wildList.add(new Food());           // error
    wildList.addAll(c);                 // error
    wildList.set(1, new Food());        // error
        
    // allowed    
    wildList.remove(1);
    wildList.remove(new Food());        
    wildList.removeAll(c);
    wildList.retainAll(c);

Reason why they are allowed or not is as follows:

Method Allowed Reason
void add​(int index, E element) No parameter has E (type parameter)
boolean add(​E element) No parameter has E
boolean addAll​(Collection<? extends E> c) No parameter has E
E set​(int index, E element) No parameter has E
E remove​(int index) Yes no E in parameter, even though return type is E
boolean remove​(Object o) Yes no E in parameter
boolean removeAll​(Collection<?> c) Yes no E in parameter
boolean retainAll​(Collection<?> c) Yes no 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 we think List<Snack>, List<String> etc., as one extreme (which can accept list of a specific type), the List<?> is the other extreme (which can accept list of any type). Can we define something in between? Yes, Generics use extends and super keywords to fine tune the variable and parameter declarations. With them we can place upper or lower bound on wildcards and next chapter explains it.