Sharing Comparable properties with generics in Java

What if you need to develop some software to compare houses to each other? A client found a nice Mansion and needs to see a list of similar buildings and the idea is that if he likes a Mansion he won’t like a Flat. So even if both have four rooms and a kitchen, they are not comparable at all. Then how do you implement Comparable in Java 5 and up?

The first thing you’d think of would be to write an explicit version for each instance, but they are all houses and all have rooms. So you might as well create an AbstractHouse class to create a compare function that compares the generic parts of an house. Let’s just focus on the comparison on the number of rooms.

Using the following code for your AbstractHouse will ensure that Mansions can only be compared to Mansions and Flats only to Flats, even if you ignore the generics in your code.

public abstract class AbstractHouse<T extends AbstractHouse> implements Comparable<T> {

    private int noOfRooms;

    public int compareTo (T o) {

        if (!getClass ().isInstance (o))
            throw new ClassCastException ("can't compare " + getClass ().getCanonicalName () + " to " + o.getClass ().getCanonicalName ());

        return this.noOfRooms - other.noOfRooms;
    }
}

As you can see, line 1 locks in the generic part of the Comparable interface, but in case you aren’t really using generics the isInstance check at line 7 will cause a ClassCastException to be thrown. Throwing a ClassCastException is conform to the Comparable interface which states that one can be thrown “… if the specified object’s type prevents it from being compared to this object.” What is left is defining the Mansion class as follows.

public abstract class Mansion extends AbstractHouse<Mansion> implements Comparable<Mansion> {
}

Although I am not sure of the necessity of explicitly stating the Comparable in the class definition, it seems rational to do so. It’s quite possible that the Comparable interface on a super class doesn’t have to mean that all subclasses share the same feature by inheritance, the JavaDocs aren’t clear on this and being explicit about it isn’t harmful.