Angelika Langer - Training & Consulting
HOME | COURSES | TALKS | ARTICLES | GENERICS | LAMBDAS | IOSTREAMS | ABOUT | CONTACT | Twitter | Lanyrd | Linkedin
 
HOME 

  OVERVIEW

  BY TOPIC
    JAVA
    C++

  BY COLUMN
    EFFECTIVE JAVA
    EFFECTIVE STDLIB

  BY MAGAZINE
    JAVA MAGAZIN
    JAVA SPEKTRUM
    JAVA WORLD
    JAVA SOLUTIONS
    JAVA PRO
    C++ REPORT
    CUJ
    OTHER
 

GENERICS 
LAMBDAS 
IOSTREAMS 
ABOUT 
CONTACT 
Java Generics - Wilcard Instantiations of Parameterized Types

Java Generics - Wilcard Instantiations of Parameterized Types
Wildcard Instantiations of Parameterized Types
 

JavaPro Online, May 2004
Angelika Langer & Klaus Kreft


 

Wildcard Instantiations of Parameterized Types
What is the purpose of wildcards?
More Details Regarding the Wildcard Language Feature
    Access Restrictions
        Rules for Unbounded Wildcards
        Rules for Upper Bound Wildcards
        Rules for Lower Bound Wildcards
Using Wildcard Instantiations
Using Wildcards With An Upper Bound
Using Unbounded Wildcards
Using Wildcards With A Lower Bound
    Remaining Limitations
Summary
References

1 Wildcard Instantiations of Parameterized Types

With the release of J2SE 1.5 (announced for August 2004) parameterized types and methods, also known as Java Generics, will be available as a new language feature of the Java programming language. We gave an overview of most of the Java Generics features in a previous article (see / PRO1 /).  This time we aim to explain wildcards – another language construct relevant in the context of Java Generics.  Wildcards can be used for instantiation of parameterized types.  In its simplest form a wildcard is a question mark “?” and permits type declarations such as List<?>, for instance.
 

1.1 What is the purpose of wildcards?

Consider a hierarchy of shape classes with an abstract superclass Shape and several subclasses such as Circle, Triangle, Rectangle, etc.  All shape classes have a draw() method.
public abstract class Shape {
   public abstract void draw();
}
public final class Circle {
   ...
   public void draw() { ... }
   ...
}
public final class Triangle {
   ...
   public void draw() { ... }
   ...
}
...
Given a collection of shapes we might want to implement a drawAllShapes() method.  How would we declare this method?  Here is a first approach :
public void drawAllShapes(Collection<Shape> c) {
   for (Shape s : c) {
      s.draw();
   }
}
This method can be invoked as follows:
Collection<Shape> c = new LinkedList<Shape>();
... fill the collection with shapes ...
drawAllShapes(c);
It does, however, disallow the following invocation:
Collection<Triangle> c = new LinkedList<Triangle>();
... fill the collection with triangles ...
drawAllShapes(c);
Although triangles are shapes and have a draw() method we cannot invoke the drawAllShapes() method supplying a collection of triangles as an argument.  This is because the method parameter is declared to be of type Collection<Shape> and the argument supplied is of type Collection<Triangle>. Both types are incompatible with each other.  More generally, there is no assignment compatibility relationship between different instantiations of the same parameterized type.  A Collection<Triangle> cannot be assigned to a variable of type Collection<Shape>.  This may or may not be in line with our intuition.  We might expect assignment compatibility, in particular in the case of collections, because collections are similar to arrays and built-in array types with different element types are assignment compatible if the element types have a super-sub-type relationship.  For instance, a method declared as taking a parameter of type Shape[] would accept an argument of type Triangle[].  This type relationship is known as covariance. It means that an array of a subtype, such as Triangle[], is assignment compatible to an array of a supertype, such as Shape[].  No such covariance relationship exists for instantiations of parameterized types.

In order to make up for the lack of covariance among instantiations of a parameterized type, the language designers invented wildcards.  Using wildcards we can declare the drawAllShapes() method as follows:

public void drawAllShapes(Collection<? extends Shape> c) {
   for (Shape s : c) {
      s.draw();
   }
}
The expression “? extends Shape” is an example of a wildcard. It stands for a family of types, namely all types that are subtypes of Shape, the type Shape itself included. Consequently, the instantiation Collection<? extends Shape> stands for the family of types that are instantiations of class Collection for an element type from the family of subtypes of Shape.

Given this parameter declaration we can invoke the drawAllShapes() method as before, supplying a collection of shapes to the method:

Collection<Shape> c = new LinkedList<Shape>();
... fill the collection with shapes ...
drawAllShapes(c);
In addition we can invoke the method with a collection containing subtypes of Shape:
Collection<Triangle> c = new LinkedList<Triangle>();
... fill the collection with triangles ...
drawAllShapes(c);
In essence, wildcards are predominantly used for definition of relatively relaxed APIs, that is, wildcards permit declaration of methods that accept a broad range of argument types – broader than would be possible without wildcards.

1.2 More Details Regarding the Wildcard Language Feature

The expression “? extends Shape” is just one example of a wildcard.  There is a total of 3 different wildcard flavors:
  • “? extends Type” is a wildcard with an upper bound; it stands for the family of types that are subtypes of Type.
  • “? super Type” is a wildcard with a lower bound; it stands for the family of types that are supertypes of Type.
  • “?” is an unbounded wildcard; it stands for “all types”.
Wildcards can be used for instantiating parameterized types, that is, we can form types such as Collection<? extends Shape>, Comparable<? super Triangle>,  LinkedList<?>. The resulting instantiations stand for families of types: Collection<? extends Shape> represents the set of collection types that have an element type that is a subtype of Shape.  Similarly, Comparable<? super Triangle> stands for instantiations of the Comparable interface that allow comparison to objects of supertypes of Triangle. LinkedList<?> comprises all instantiations of the parameterized type LinkedList, regardless of the element type.

The wildcard instantiations of parameterized types can be used as argument and return types of methods, as type arguments to other parameterized types and for declaration of variables.  They can not be used for creation of objects or arrays, that is, a wildcard instantiation is not permitted in a new expression.  Wildcard instantiations are not types, they are placeholders for a member from a family of types.  In a way, a wildcard instantiation is similar to an interface:  we can declare variables of interface types, but we cannot create objects of interface types; the created objects must be of a class type that implements the interface.  Similar with wildcard instantiations: we can declare variables of a wildcard instantiated type, but we cannot create objects of such a type; the created objects must be of a concrete instantiation from the family of instantiations designated by the wildcard instantiation.  Here is an example:

LinkedList<?> list1 = new LinkedList<String>();  // fine
LinkedList<?> list2 = new LinkedList<?>();  // error: cannot create objects of a wildcard type

LinkedList<? extends Shape> list3 = new LinkedList<Triangle>(); // fine
LinkedList<? extends Shape> list4 = new LinkedList<String>(); // error: does not belong to the family
 

1.2.1 Access Restrictions

Reference variables or parameters of wildcard type refer to an object of a concrete type.  Access to the referenced object via a reference variable or parameter of a wildcard type is restricted. The restriction is different depending on the kind of wildcard that is being used.

1.2.1.1 Rules for Unbounded Wildcards

A “?” wildcard prohibits invocation of methods that take arguments of the type that the wildcard stands for.  For instance, the wildcard List<?> stands for an arbitrary instantiation of the parameterized type List and the element type is unknown; it can be any type.  When we try to add an element to a list of elements of unknown type then there is no way of telling whether the element to be added is of the right type.  Here is an example to illustrate the situation. A reference variable of type List<?> refers to an object of type that is a concrete instantiation of the parameterized type List:
List<?> list = new LinkedList<String>();
In our example, the reference variable list refers to a list of strings.  If we were allowed to invoke the add() method through a List<?> reference variable we could attempt to insert any type of element into the list, like in the example below:
void f(List<?> list) {
   list.add(new Date());  // error: must not invoke methods that take arguments of the „?“ type
}
Method f(List<?> list) is declared to accept any instantiation of List. There is no way the compiler can know what concrete instantiation of List the parameter of type List<?> refers to.  Hence, the compiler must not permit invocation of the add() method, because it would permit inserting alien elements into the list.

The general rule is that through a reference variable of an unbounded wildcard type we cannot invoke any method of the referenced object that takes an argument of the unknown type that the “?” wildcard stands for.  The only exception from this rule is invocation with null as an argument, because null has no type.  Hence the following invocation would be permitted:

void f(List<?> list) {
   list.add(null);  // fine
}
Conversely, methods that return an object of the unknown type may be invoked through a reference variable of a unbounded wildcard type.
void f(List<?> list) {
   Object ret = list.get(0); // fine
}
The invocation of the get() method is permitted because receiving elements of unknown type does not create any problems; no matter what the type of the elements stored in the list may be, the elements are assignment compatible to Object.

1.2.1.2 Rules for Upper Bound Wildcards

Similar rules apply to the other wildcard types.  Via a reference variable of an “extends” wildcard type we cannot invoke any method of the referenced object that takes an argument of the unknown type that the “extends” wildcard stands for, except null. Invocation of methods that return an object of the unknown type may be invoked.  The example below illustrates the restrictions:
void f(List<? extends Shape> list) {
   list.add(new Triangle());  // error: must not invoke methods that take arguments of the „? extends Shape“ type
}
Again, the compiler does not know whether the reference variable of type List<? extends Shape> refers to a list of triangles, as is assumed in the invocation of the add() method.  The reference variable of type List<? extends Shape> may equally well refer to a list of circles or rectangles, in which case the insertion of a triangle into the list would be harmful.

Again, retrieving an element from the list is safe, because all elements in the list are guaranteed to be Shapes.

void f(List<? extends Shape> list) {
   Shape ret = list.get(0);   // fine
}
Note, that for an “extends” wildcard type, different from an unbounded wildcard type, we have more knowledge regarding the type of the returned object.  We know that the returned object is at least a Shape, not just an Object.

1.2.1.3 Rules for Lower Bound Wildcards

Different rules apply for “super” wildcards.  Here it is the other way round: invocation of methods that return an object of an unknown type is restricted and only allowed if we make no assumptions regarding the returned type and treat the returned object as if of type Object. Invocation of methods that take arguments of the unknown type is permitted without any restrictions.  For illustration, here is an example:
void f(List<? super Triangle> list) {
   Triangle ret = list.get(0); // error: must not invoke methods that return the „? super Triangle“ type
   Object   obj = list.get(0); // fine
}
The compiler has no way of knowing whether the reference of type List<? super Triangle> refers to a list of triangles, as the first  invocation of the get() method suggests.  The references variable may well refer to a list of Shapes and might contain a mix of various types of shapes.  Hence, what is returned from the get() method may be a circle or a rectangle and not a triangle.  The call to get() is only permitted if we treat the return value as an Object, thus making no assumptions regarding its type.

Addition of elements to the list is permitted, but it must be visible to the compiler that the elements are within bounds.  Here is an example:

void f(List<? super Triangle> list) {
   list.add(new Triangle());  // fine

   Shape shape = new Triangle();
   list.add(shape);  // error: argument must be of type Triangle
}

We may add a triangle to the list, but only if we pass it to the add() method through a reference of the wildcard’s lower bound, namely type Triangle in our example.  If we supply a reference variable of a supertype of Triangle to the add() method, like for instance a reference of type Shape, then the compiler cannot know whether the Shape reference refers to a Triangle or a Circle or another subtype of Shape.  For this reason the call to add() is rejected unless the argument is of the type that is specified as the wildcard’s lower bound.
 

2 Using Wildcard Instantiations

Let us explore typical use of the different types of wildcards.

2.1 Using Wildcards With An Upper Bound

We already saw a typical example for use of a wildcard instantiations, when we discussed the drawAllShapes() method.  If we want that the drawAllShapes() method  accepts not only collections of shapes but also collections of subtypes of Shape, then we can achieve this by declaring the argument as a Collection<? extends Shape>.
public void drawAllShapes(Collection<? extends Shape> c) {
   for (Shape s : c) {
      s.draw();
   }
}
Collection<Triangle> c = new LinkedList<Triangle>();
... fill the collection with triangles ...
drawAllShapes(c);
Essentially, the wildcard is used to relax the requirements imposed on the method arguments and to permit a broader range of argument types.

Programmers familiar with parameterized methods might have noticed that the same effect can be achieved without wildcards as well:  if we declare the drawAllShapes() method as a parameterized method we would also allow collections of subtypes of Shape as arguments.  The parameterization would look like this:

public <T extends Shape> void drawAllShapes(Collection<T> c) {
   for (Shape s : c) {
      s.draw();
   }
}
The parameterized drawAllShapes() method accepts collections whose element type is a subtype of Shape, exactly as the non-parameterized version with the wildcard instantiation does.  The difference is that the parameterized version does not restrict access to the method argument, while the wildcard instantiations disallows the invocation of methods that take arguments of the unknown type. In this example, it does not make any difference, because the only method we invoke on the Shape type (or subtype thereof) is method draw(), which does not take any arguments.  In another context the parameterized method might be more flexible than its non-parameterized counterpart with the wildcard argument. The point to take home is: wildcards with an upper bound can often be replaced by parameterization with a bounded type parameter.
 

2.2 Using Unbounded Wildcards

Unbounded wildcards are used when the type of the type parameter does not matter, either because the object referred to is not used or because no methods of that type are invoked.  An example of using an unbounded wildcard would be a method that prints all elements in a collection, as shown below:
void printAll(Collection<?> c) {
   for (Object o : c)  System.out.println(o.toString());
}
All that is required of the elements in the collection is that they must have a toString() method.  Since the toString() method is defined in class Object, this is not much of a requirement.  Basically we need not know anything about the type of the objects contained in the collection.  Hence a declaration as Collection<?> is appropriate.

Note the difference between use of a Collection<?> and use of a Collection<Object>: a method declared as void printAll(Collection<Object> c) would not accept a LinkedList<String> as an argument, while a method declared as void printAll(Collection<?> c) permits it.  Both expressions look like they denote a “collection of anything”.  The difference is that Collection<Object> is a concrete type, while Collection<?> is not a type.  It just stands for a representative from the family of types that are instantiations of Collection.  Hence a method parameter declared as Collection<Object> must be of exactly that type, or a subtype thereof.  A method parameter declared as Collection<?> can be of any type that is an instantiation of Collection, or a subtype of any of these types.

The type Collection<?>  is also different from the raw type Collection.  Invocation of methods that would be flagged as errors when invoked via a reference of type Collection<?> would lead to “unchecked warning” when invoked via a reference of type Collection.  Also, the raw type is a concrete type, of which objects and arrays can be created, while Collection<?>  must not appear in new expressions.

Unbounded wildcards are also used for partial instantiations and express that no requirements are imposed on a type. Here is an example.  Say, we have a parameterized pair class with two fields of different unknown types:

class Pair<S,T> {
   private S first;
   private T second;
   ...
   <A extends S> void setFirst(Pair<A,?> p) {
     first = p.first;
   }
   ...
}
The parameterized class Pair<S,T> has two instance fields of type S and T respectively. The declaration of the parameter of method setFirst() as a Pair<A,?> indicates that the method has no interest in the second instance field of the pair.  The type Pair<A,?> would denote a partial instantiation of the parameterized Pair type.
 

2.3 Using Wildcards With A Lower Bound

Wildcards with a lower bound are used for a similar purpose as wildcards with an upper bound: they are used as arguments types of methods and relax the requirements imposed on the argument type.  Let us take a look at an example. Consider a class hierarchy of value types such as the following:
class Person {
   private String firstName;
   private String lastName;
   private Date   dateOfBirth;
   ...
}
class Employee extends Person {
   private Person Supervisor;
   ...
}
If the classes in such a hierarchy of value classes were supposed to implement a parameterized interface such a Comparable we run into the problem that super- and subclass cannot implement different instantiations of the same parameterized interface, although semantically it would make a lot of sense.  What we would like to do – but are not permitted to do - is the following:
class Person implements Comparable<Person> {
   private String firstName;
   private String lastName;
   private Date   dateOfBirth;
   ...
   public int compareTo(Person other) {
     ...
   }
}
class Employee extends Person implements Comparable<Employee> {  // error: already implements Comparable
   private Person Supervisor;
   ...
   public int compareTo(Employee other) {
     ...
   }
}
The compiler rejects the declaration of the subclass because it would implement different instantiations of the same interface, namely Comparable<Person> and Comparable<Employee>, which is not permitted.  The restriction stems from the fact that parameterized types and methods are translated by type erasure.  In the process of type erasure the two instantiations of the parameterized interface are mapped onto the raw type and there would be no longer a difference between the interface that the super- and subclass implement.

The result is that the superclass determines which instantiation of a parameterized interface all its subclasses implement.  This, on the other hand, renders it impossible to pass the subclass to certain methods, unless the methods use wildcards with lower bounds.  Below is an example of such a method. But  let us first fix our class hierarchy, so that all classes in the hierarchy of  Person classes would implement Comparable<Person>:

class Person implements Comparable<Person> {
   private String firstName;
   private String lastName;
   private Date   dateOfBirth;
   ...
   public int compareTo(Person other) {
     ...
   }
}
class Employee extends Person {
   private Person Supervisor;
   ...
   public int compareTo(Person other) {
     ...
   }
}
Now, consider a method that asks for comparable objects:
<T extends Comparable<T>> void someMethod(T arg1, T arg2) {
   ... arg1.compareTo(arg2) ...
}
This method can be invoked with reference variables of type Person as arguments, but an argument of type Employee would not be accepted, because an Employee is comparable to Person, but not to an Employee.  In other words, the Employee would not be within bounds.  The code snippet below illustrates the situation:
Person p1 = new Employee(“Peter”, “Miller”, new Date(4,3,69));
Person p2 = new Employee(“Jim”, “Anderson”, new Date(10,8,84));

someMethod(p1, p2);  // fine

Employee e1 = new Employee(“Peter”, “Miller”, new Date(4,3,69));
Employee e2 = new Employee(“Jim”, “Anderson”, new Date(10,8,84));

someMethod(e1, e2);  // error:  arguments are not within bounds

As the example demonstrates, it is essential that methods that take parameters through a parameterized interface type are declared to accept upper bound wildcard instantiations of the parameterized interface instead of concrete instantiations.  Otherwise, applicability of the method would be severely – and unnecessarily – restricted.

Let us relax the method signature. If we declare the method using an upper bound wildcard instantiation – instead of the concrete instantiation - the method accepts super- and subclass objects as arguments, that is, Person and Employee objects would be permitted.

<T extends Comparable<? super T>> void someMethod(T arg1, T arg2) {
   ... arg1.compareTo(arg2) ...
}
The example nicely illustrates the need for lower bound wildcards. They help solving a problem that would otherwise remain insurmountable.  Classes in a class hierarchy can never implement different instantiation of the same parameterized interface; they must implement the same parameterized interface, namely the one that the topmost superclass decided to implement.  Methods taking arguments that must implement a concrete instantiations of the parameterized interface type would accept only superclass objects. With the use of lower bound wildcard instantiations these methods are substantially more useful.

Note also, that lower bound wildcards – different from upper bound wildcards – cannot be replaced by a parameterization of the method itself.  This is because type variables can only have upper bounds, but no lower bounds, while for wildcards both variants exist.

2.3.1 Remaining Limitations

While the upper bound wildcard solves the problem discussed above, there are still cases, in which not even wildcards come to rescue.  If the interface defined a method that returned a value of the wildcard type, instead of taking it as an argument, then we would be limited by the rule that methods returning values of the unknown type cannot be invoked through a reference to a lower bound wildcard.  Here is an example using a parameterized interface Copyable that has a value returning method copy() and a hierarchy of classes that implement the interface :
interface Copyable<T> {
   T copy();
}
class Person implements Copyable<Person> {
   ...
   public Person copy() { ... }
}
class Employee extends Person {
...
   public Employee copy() { ... }
}
As discussed earlier, a method such as the following would be problematic:
<T extends Copyable<T>> T produceCopy(T arg) {
   return arg.copy();
}
This method does not permit Employees as arguments:
Person p1 = new Employee(“Peter”, “Miller”, new Date(4,3,69));

Person p2 = produceCopy(p1);// fine

Employee e1 = new Employee(“Peter”, “Miller”, new Date(4,3,69));

Employee e2 = produceCopy(e1); // error:  arguments are not within bounds

Use of a lower bound wildcard would in theory allow Employees as arguments, but would disallow invocation of the copy() method inside method produceCopy():
<T extends Copyable<? super T>> T produceCopy(T arg) {
   return arg.copy();  // error: type „? super T“ incompatible with type T
}
The value returned from the copy() method would be of type “? super T”, which means it can be of any supertype of T.  Arbitrary supertypes of T are not necessarily assignment compatible to the declared return type T, and for this reason the compiler rightly complains.
 

3 Summary

Wildcards are part of the language extension known as Java Generics and added as a new language feature in Java 1.5.  Wildcards can be used as the type argument of a parameterized type and represent a member from a family of types.  Three different types of wildcards exist: the unbounded wildcard, wildcards with a lower and wildcards with an upper bound.  Wildcard instantiations of parameterized types are typically used as parameter and return types in method declarations and permit a broader range of arguments than would be possible without use of wildcards.
 

4 References

 
/JDK15/  Java TM 2 SDK, Standard Edition 1.5.0
Update 1
http://java.sun.com/developer/earlyAccess/j2sdk150_alpha/
/TUT/  Java Generics Tutorial
Gilad Bracha
http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
/FAQ/ Java Generics FAQ
Angelika Langer
http://www.AngelikaLanger.com/GenericsFAQ/JavaGenericsFAQ.html
/LAN/  Links related to Java Generics
Further references to articles, tutorials, conversations and other information related to Java Generics can be found on this website at http://www.AngelikaLanger.com/GenericsFAQ/JavaGenericsFAQ.html .
/PRO1/ Java Generics Language Features 
Klaus Kreft & Angelika Langer
JavaPro Online, March 2004
The predecessor of this article providing an introduction to Java Generics.
http://www.AngelikaLanger.com/Articles/JavaPro/01.JavaGenericsIntroduction/JavaGenerics.html
/PRO2/ Wildcard Instantiations of Parameterized Types
Klaus Kreft & Angelika Langer
JavaPro Online, May 2004
The version of this article as published at the JavaPro Online site.
http://www.ftponline.com/javapro/2004_05/online/kkreft_05_19_04/

 
 

If you are interested to hear more about this and related topics you might want to check out the following seminar:
Seminar
 
Effective Java - Advanced Java Programming Idioms 
5 day seminar (open enrollment and on-site)
 
 
  © Copyright 1995-2012 by Angelika Langer.  All Rights Reserved.    URL: < http://www.AngelikaLanger.com/Articles/JavaPro/02.JavaGenericsWildcards/Wildcards.html  last update: 4 Nov 2012