Generics and Wildcards: Part 1
- January 2nd, 2010
- Posted in Generics . Java
- Write comment
Wildcards have been introduced to Java some years ago (in JDK 1.5). And they are easy to understand – as long as no collections are involved. Then everything becomes a little bit more complex…
This (and the following) blog posts will try to show in simple steps how to handle wildcards in collections correctly.
Collections + Generics = non intuitional
Every Java developer knows that an Integer is also a Number since Integer extends Number. So every time someone expects a Number (e.g. as method or constructor parameter), I may call the method with a parameter of type Integer (or Double, or…).
Every time I say Number, I literally mean:
An object of type Number or of any other class extending Number.
And every Java developer is used to understand it this way (ok, there might be some that don’t, but hey, those won’t read that blog post…).
When it comes to collections, most of the Java developers unconsciously transfer that understanding. At first glance it seems to be the same thing…
Set extends Collection: So when a method parameter is declared as Collection, it might be called using a Set (or List, or…).
Collections and their content
But collections don’t just have a type. They also contain objects. And those objects also have a type. And that is the point where the trouble begin. The first step seems to be very obvious.
List<Integer> is just a List containing Integers. List#add accepts an Integer, List#get returns an Integer…
And of course the same can be said about a List of Numbers:
List<Number> is just a List containing Numbers. List#add accepts a Number, List#get returns a Number…
The difference between a List<Integer> and List<Number> is, that the former may contain just Integers (and sub types) while the later might contain also Doubles, Longs and so on.
Collections: The difference
Following the usual experience, one might think, that that assignment works:
1 2 | List<Integer> integerList = new ArrayList<Integer>(); List<Number> numberList = integerList; //!!!! Does not work !!!!! |
This assignment shouldn’t be problematic concerning read access. Every read access should return one or more Integers. And since Integer extends Number everything seems to be ok.
1 2 3 | List<Integer> integerList = new ArrayList<Integer>(); List<Number> numberList = integerList; //!!!! Does not work !!!!! Number n = numberList.get(0); //works |
But what’s about write access?
The compiler allows you to add any object of type Number to be added to numberList (List<Number>). But of course it must not be possible to add an object of type Number to List<Integer>.
1 2 3 | List<Integer> integerList = new ArrayList<Integer>(); List<Number> numberList = integerList; //!!!! Does not work, because of next line! numberList.add( new Double(2.0 )); //We add a double to List<Integer>!!!! |
Fruits…
A list of fruits may contain all kinds of fruits. Apples, strawberries and so on. A list of apples may only contain apples. Therefore a list of apples must never be cast to List<Fruit>.
The solution (at first non intuitional, too)
Some smart minds at Sun discovered that problem. And they also introduced the solution: Wildcards. Just accept wildcards as necessary – because they are.
First the example:
1 2 3 4 |
We may assign List<Integer> to List<? extends Number> since Integer extends Number.
The declaration List<? extends Number> may be read as: A list of an unknown subtype of Number.
Of course read access is possible. We now that the containing elements must be of type Number. But adding is not possible since the exact type of the list is unknown.
No comments yet.