This is the first part in what is planned to be a loosely-coupled series of articles on current developments in mainstream programming languages.
Topics include:
- Evolution of Java
- New abstractions in programming languages
- The functional turn
- Scala: „The next programming language™”
Generics in Java
When I started to program Java 5 professionally after some years of blissful absence from the Java world, I thought myself to be well-prepared for generics. After all, I had done metaprogramming in both C++ and Python for several years.
Of course, experience never saves you from the perils of learning. It took some time until I finally got generics, including the common misunderstandings about covariance and the like. Fortunately, in the project I was working on at the time, we were allowed to go wild and try out all new features in Java 5 at length. We were the first ones to work with the new version and also carrying out the internal training, so we really had to understand what generics were about, and why all tutorials usually contain more don’ts then dos.
When I finally understood them, I was really disappointed. The type system wasn’t generic at all! Type annotations are just some sugary coating stripped out by the compiler after the program passes the type checks. Still, generics proved to be useful from time to time. Some problems just kept coming back, and I will briefly outline them there.
The ugly cast
There is going to be an ugly cast at some point, where you (the programmer) know more about the static or runtime type of an object than the compiler. Our strategy was to isolate ugly casts in minimally small methods with @SuppressWarnings("unchecked") annotations. One prominent example is serialization:
@SuppressWarnings("unchecked") public List<String> foo(ObjectInputStream i) throws IOException { return (List<String>) i.readObject(); }
Generic types + class objects
A lot of generics pain is remedied when you always hand around Class<V> objects whenever you create instances of classes with generic type arguments. This is often cumbersome, as it tends to make your APIs larger, but at least provides some kind of runtime type safety.
We used this often enough to call it a pattern, though I think we never gave it a proper name.
Sun’s Java compiler
We were in for some hard lessons when we found out that Eclipse’s Java compiler was much better at type inference than Sun’s javac. These kinds of errors were especially hard to track down, and some of them were unfixed at least up to 1.5.0 Update 12.1
The backlash
Now, after Java generics have been in the wild for a little more than three years and presumably have seen a wider adoption, a backlash is forming. While early coverage was mostly apologetic of all the oddities that had to be introduced to keep bytecode compatibility2, a lot of complaints about the (perceived) complexity of generics is heard.
Before I’m going to dive into an example, let me state the following:
- Yes, Java code does get uglier and less readable with generics,
…but a lot of that could be addressed with typedef’s. - Yes, generics have a lot of gotchas,
…but most of them are due to backwards compatibility. I would have liked to hear the millions of IDE monkeys cry in horror if BC had been broken. - Yes, generics are difficult to grasp.
Get over it. Seriously.
Killing Wildcards
In his recent article „Simplifying Java Generics by Eliminating Wildcards”, Robert Lovatt argues that Java generics could be simplified by removing wildcards altogether and make covariant generic types the default behavior, similar to arrays.
Please note that the following code examples assume that you have read the article.
In arrays, we observe the following behavior:
List[] listArray = new List[10]; Collection[] collArray = listArray; collArray[0] = new HashSet(); // will result in an ArrayStoreException
His argument is that this behavior could simply be adopted for generics, making this code compile:
List<List> listList = new ArrayList<List>(); List<Collection> collList = listList; collList.add(new HashSet());
Now, suppose the compiler would accept this piece of code (which it doesn’t), how should an exception similar to ArrayStoreException be thrown? The generic types are not known at runtime, in contrast to arrays3 , since they couldn’t be added without braking backwards compatibility. The only way to ensure the type safety is to have the class object inside List and check if newly added objects have the correct type, laying the burden of type checking on the class designer. While this may be acceptable for the standard library, it is certainly not acceptable for general usage.
An example taken from Lovatt’s article to display the lacking power of generics in Java (and Scala) is:
ListScalaStyle<Integer> iList = new ListScalaStyle<Integer>(); ListScalaStyle<Number> nList = iList.prepend( (Number)2.0 ); // OK ListScalaStyle<Integer> iList2 = nList.tail(); // Error, still a Number list
This is exactly the pathological case of the ugly cast. You, the programmer, know the static type of something and expect the compiler to be able to infer it as well.
To show why this cannot work in general, I’ll use a trick I’ve found to be very helpful: adding a little bit of randomization.
public ListScalaStyle<Number> getListWithTail() { if(Math.random() > .5) { ListScalaStyle<Integer> iList = new ListScalaStyle<Integer>(); return iList.prepend((Number)2.0); } else { ListScalaStyle<Double> iList = new ListScalaStyle<Double>(); return iList.prepend((Number)2.0); } } // ... // can never work ListScalaStyle<Integer> iList2 = nList.tail(); // Error, still a Number list
This little example is of course not a total refutation – having the compiler being able to infer more type information statically might always be useful. However, it will always be limited to small pieces of code. It also forces the compiler to actually examine the bytecode of functions in order to see the flow of objects, because prepend might do something wildly different. This removes many advantages of polymorphism, a technique at the very heart of Java.
Conclusion and Outlook
With generics, Java gets more complicated. It allows programmers to make interesting abstractions, but also freely hands out all kinds of guns for shooting yourself in the foot. This is definitely a deviation from Java’s original design principles and, ironically, makes it a bit more like C++ – something which the designers tried to avoid as hard as possible.
In the upcoming articles, we will examine the question why having generics this way still might be a good idea (though for totally different reasons), why the Java designers did it in the first place, and what the generics disaster (Tim Bray) teaches us about the design and evolution of programming languages.
- most of them are fixed in Java 6 [↩]
- see Generics gotchas for a good example [↩]
- see the documentation of Class.getComponentType() [↩]

![Validate my RSS feed [Valid RSS]](http://shlomme.diotavelli.net/images/valid-rss.png)
1 response so far ↓
1 aleks // Mai 5, 2008 at 17:57
Thanks for the link to “Simplifying Java”, it was an interesting read (and seriously, even the examples on that page are well thought-out and nicely crafted! How swell).
Although I like the concept as a Gedankenexperiment, I would hate to see wildcards go – writing gneric [sic!] data structures in library backends (I’m currently implementing Sedgewick’s left leaning red-black trees, for example) would be a pain without wildcards and the more complex aspects of the generalized type system in Java.
With respect to the title of your post, let me add the following: Rescue Java’s generics by abolishing erasure. Erasure really is nothing more than a backwards compatibility device. While BC is always a good thing, it’s time for people to move past 1.4, and there are serious consequences of erasure, as in: you can’t make static arrays out of parameterized classes. You also can’t make static arrays of inner classes of parameterized classes. I’ve hit that issue. Hard. (“generic array creation” complier errors, ftw!) The only valid workaround for the latter case is either an ugly cast or wrapping it up in a List-class of some sort (which, internally, does an ugly cast or still does it the 1.4 way of casting around Objects, so it’s of no use implementing it this way).
Leave a Comment