Java Generics for Compare

I’ve been developing with Java 5+ for quite a while now. Not all developers are this lucky, some are still stuck with 1.4… some even with 1.3! But my clients all made the excellent step forward to Java 5 (some even to 6). The problem is, they moved the runtime/JDK but forget to move their developers!

In Java 5 the language brings some good improvements, the for-loop is easy to understand, and almost all the developers are using this by now. The problem starts with generics. There is a part most developers understand, the Collections API. Almost all programmers use lists now as: List instead of a plain old List. This is a good start, but it must not end here! First, I must admit, generics in Java can sometimes be hard and confusing (when using <? extends X> and <? super X>). So I'm not going to talk about any of this 'hard stuff'. Its the use of 'easy' generics that can our lifes so much easier.

For example the piece of code below:

public class LabelPlaceholderComparator implements Comparator { 

    /**{@inheritDoc */
    public int compare(Object o1, Object o2) {
        LabelPlaceholder p1 = (LabelPlaceholder) o1;
        LabelPlaceholder p2 = (LabelPlaceholder) o2;
        return p1.getLabel().compareTo(p2.getLabel());
    }
}

Of course, there seems to be not much wrong with the code, I see it all the time. Yes, the code breaks if you put something else in the comparator, but hey… the Javadoc says it only accepts LabelPlaceholders! So lets use this code:

List holders = fillList();
Collections.sort(holders, new LabelPlaceholderComparator());

Done! Its working and no problems right? Not quite, your IDE (Eclipse in my case) complains about this code.
For example Eclipse says:

Type safety: The expression of type LabelPlaceholderComparator needs unchecked conversion to conform to Comparator<? super T>

At this point, most programmers at the company I work for now will just ignore this warning. They might even add:
@SuppressWarnings(“unchecked”)

What a shame… Lets just examine this warning, what is Eclipse trying to tell us here? The compiler doesn’t know we created the Comparator with only LabelPlaceholders in mind. But the compiler does know (with generics) that the List only contains LabelPlaceholders. So the warning is (in understandable English):

I’ve got a list here of T (LabelPlaceholders) and a Comparator for Objects, this can go wrong! I’d rather have a specific Comparator for this job. Do you have one for me?

The solution to this problem is very simple, but most neglect to use it:

public class LabelPlaceholderComparator implements Comparator<LabelPlaceholder> { 

    /**{@inheritDoc */
    public int compare(LabelPlaceholder p1, LabelPlaceholder p2) {
        return p1.getLabel().compareTo(p2.getLabel());
    }
}

As you can see, the code is much smaller. The interface is now generified, it knows we are going to compare LabelPlaceholders now, nothing more, nothing less. Also, we don’t have to cast anymore, because of the generics you can’t put anything else in there.

So, lets go to the conclusion: Why is the latter code better code?

  1. As you can see, the code is smaller!
  2. There are no casts, the code is safer (no ClassCastException or eleborate class checks)
  3. If somebody uses your code, he/she knows what kind of objects the Comparator can handle. You don’t have to read the Javadoc or the code to see what it does.

Throughout the projects I encounter I keep finding examples of places where generics would have made the code smaller/safer/more understandable. For some reason the programmers still only use generics on collections. So, even though generics aren’t perfect, please use them where/when you can, it’ll always add clarity to the code, and most of the time it’ll also make your code safer, and in some cases the code gets smaller because you can leave away casts and class-checks.

Don’t ever let me see public int compare(Object o1, Object o2); again!

(You see, it is possible for me to have a discussion about Java generics without mentioning reified generics!)