Nothing Compares to You

From EggeWiki

One of my frequent pain points with Java Generics concerns the Comparable interface. In pre-generics, the Comparable interface can be specified at multiple points within an inheritance hierarchy. Basically, you should only implement comparable in final classes. This is why the Number interface does not implement Comparable, but all of it's implementing classes do.

Consider this Java 1.4 example, where I declare two interfaces. These interfaces allow me to compare mammals to mammals and birds to birds. <geshi lang="java5"> public interface Bird extends Comparable { double weight(); } public interface Mammal extends Comparable { double speed(); } </geshi>

In Java 1.4, you can create a Platypus, which implements both of these interfaces. It's allowed to do this, because it uses the raw type comparable, and only has to implement compareTo(Object o). Here's an example, of how platypus can be included in Bird collections and Mammal collections.

<geshi lang="java5"> public class SortBirdsAndMammals {

private static class Penguin implements Bird { public double weight() { return 35; } public int compareTo(Object o) { return Double.compare(weight(), ((Bird)o).weight()); } }

private static class Platypus implements Bird, Mammal { public double weight() { return 10; } public int compareTo(Object o) { if (o instanceof Bird) return Double.compare(weight(), ((Bird)o).weight()); if (o instanceof Mammal) return Double.compare(speed(), ((Mammal)o).speed()); return 0; } public double speed() { return 10; } }

private static class Dolphin implements Mammal { public double speed() { return 40; } public int compareTo(Object o) { return Double.compare(speed(), ((Mammal)o).speed()); } }

public static void main(String[] args) { List<? extends Bird> birds = Arrays.asList( new Penguin(), new Platypus() ); Collections.sort(birds);

List<? extends Mammal> mammals = Arrays.asList( new Dolphin(), new Platypus() ); Collections.sort(mammals); } } </geshi>

Now, if I decide to make Comparable type safe I would make these changes: <geshi lang="java5"> public interface Bird extends Comparable<Bird> { double weight(); } public interface Mammal extends Comparable<Mammal> { double speed(); } </geshi>

This will cause my subclasses to break, until I fix their compare methods. Everything would work fine if then Platypus could define two compare methods - one for birds and one for mammals. But, do to type erasure, this is not allowed. If some future version of Java brings us reifiable generics, the above change should be possible. Until then, for Comparable, either don't use generics with it, or don't use it in inheritance hierarchies.