Wednesday, July 04, 2007

Generics

In my last post, I discussed annotations, and their contributions to the Java language. The short version: they are pretty simple, and surprisingly useful. Generics, also introduced with Java 1.5, is a much larger pill to swallow. Not on the useful part, they are undeniably helpful. They do something very important, which is to make run-time errors visible at compile time, while at the same time increasing the self-documentation of code. This is a great thing. However, there is no doubt that they add complexity. To even document all cases of complexity would take a whole blog dedicated to just that subject. I'll give one example:

foo(List<B> list);
bar(List<? extends B> list);
tor(List<? super B> list);
So, what's the difference between these? Well, suppose B has a subtype C, and a supertype A. So the inheritance hierarchy goes A -> B -> C. You can call foo with a List<B>, but not a List<C>. But you can call bar with a List<C>; as well as List<B>. You can call tor with a List<A> and List<B>. Keep in mind that the contents of the list are not what I am talking about, just the compile-time type of the list.

Generics are a bit more complicated than most people assume. Almost anyone who has spent time with Generics has gotten into several puzzling situations that they can't explain, and where the solution isn't obvious.

The question I wonder about is whether it has done more harm than good. This is not a matter of opinion, I think. If we could count up all the hours of lost productivity in wrestling with generics, and count the hours saved by catching bugs and improving the clarity of the code, would the time saved exceed the time lost? My anecdotal evidence is that it is a net benefit. It has rescued the Java world from the perils of type-casting, and I do remember getting many ClassCastExceptions in the past. Nowadays they are rare, and I don't spend excessive time on the complexities.