Good article on Java5 Generics by Martin Wolf:
http://blogs.infosupport.com/martinw/articles/generics.aspx
It seems many programmers are confused about generics, in particular the use of the ? extends ...
notation. The question mark is called a type wildcard, and is typically used as the value of a type parameter in a generic method. It means that wherever the method is invoked in your code, the compiler infers a specific type to be substituted for the wildcard and enforces that at compile time. The notation ? extends X
is a bounded wildcard, meaning that the deduced type must be a subtype of X
. Here’s a variation on Wolf’s example which I think might help clarify the difference.
Say you have a class Animal
which supports the method feed()
, with subclasses Cat
and Dog
. Consider this code which does not use generics:
public static void feedAnimals1(List<Animal> pets) { for (Animal a : pets) { a.feed(); } for (int i=0; i < pets.size(); i++) { pets.set(i, (i % 2 == 0) ? new Cat() : new Dog()); } }
The argument pets
is of type List<Animal>
. That means that each element can be a Cat
or a Dog
object. They all understand the feed()
method, and any element can be reassigned to a Cat
or Dog
object (since the elements are of type Animal
).
But what if we write it this way instead?
public static void feedAnimals2(List<? extends Animal> pets) { for (Animal a : pets) { a.feed(); } for (int i=0; i < pets.size(); i++) { pets.set(i, (i % 2 == 0) ? new Cat() : new Dog()); // compile error } }
This second version uses generics. In this case we are not saying that the input must be of type List<Animal>
. Rather, we are saying that the input must be of type List<X>
where X is any subtype of Animal. At each location in the code where this method is called, the Java compiler will guess a suitable type for X at compile time and enforce it. It’s exactly as though you had written different overloaded versions of this method, one for lists of Cat objects, one for lists of Dog objects, and so on.
In the generic example the compiler will not let you randomly assign dogs and cats to the list elements, because the caller might legitimately pass in a list of Dog objects in which case the cat assignment would be illegal, and vice versa. But apart from that restriction, the generic version is more flexible. It can work on inputs of type List<Animal>
, List<Dog>
, or List<Cat>
e.g.
List<Dog> pets = new ArrayList<Dog>(); pets.add(new Dog()); pets.add(new Dog()); feedAnimals1 (pets); // compile error - pets must be of type List<Animal> feedAnimals2 (pets); // ok
The Sun tutorial on generics explains all in detail:
Also of interest may be the GJ project of Wadler/Odersky/Bracha/Stoutamire, whose success at forming the basis for official Java Generics may have as much to do with their motto (“Making Java easier to type, and easier to type”) as their being superstar type theoreticians:
http://homepages.inf.ed.ac.uk/wadler/gj/