Search code examples
javaequalscompareto

A cleanly overridable compareTo() method?


Having to, for the first time, define relationships amongst related objects, I found myself spending an entire weekend scouring the web for information pertaining to cleanly overridable implementations for equals() and compareTo(). Having found very little helpful information, I determined myself to find a solution. I believe the following to be a manifestation of that solution in terms of the compareTo() method. I have an idea that a similar technique might work for the equals() method as well.

My hope is that someone smarter than me might have the time to verify these findings and provide feedback regarding any pitfalls which may be encountered.

// The name chosen for the following class shell ("Base") is intended to
// portray that this compareTo() method should be implemented on a base class
// as opposed to a subclass.
public class Base
implements Comparable<Base>
{
    /**
     * Compares this Base to the specified Object for semantic ordering.
     *
     * @param other The Object to be compared.
     *
     * @return An int value representing the semantic relationship between the
     *         compared objects. The value 0 is returned when the two objects
     *         are determined to be equal (as defined by the equals method).
     *         A positive value may be returned if the "other" object is a
     *         Base and the "exact types" comparison determines this Base to
     *         have a higher semantic ordering than the "other" object, if the
     *         "other" object is not a Base, or if the "other" object is a
     *         subclass of Base who's compareTo method determines itself to
     *         have a lower semantic ordering than this Base. A negative value
     *         may be returned if the "other" object is a Base and the
     *         "exact types" comparison determines this Base to have a lower
     *         semantic ordering than the "other" object or if the "other"
     *         object is a subclass of Base who's compareTo method determines
     *         itself to have a higher semantic ordering than this Base.
     */
    public int compareTo(Base other)
    {
        int relationship = 0;

        if (other == null)
            throw new NullPointerException("other: Cannot be null.");

        if (!this.equals(other))
        {
            if (this.getClass() == Base.class)
            {
                if (this.getClass == other.getClass())
                    relationship = // Perform comparison of exact types;
                else
                    relationship = -1 * other.compareTo(this);
            }
            else
                relationship = 1;
        }

        return relationship;
    }

Solution

  • This will not work. Consider two classes, A and B that directly extend Base and whose instances are never equal. Then two instances Base a = new A(); and Base b = new B(). Since this.getClass() == Base.class will be false for both a and b, it will be the case that a.compareTo(b) == 1 and also that b.compareTo(a) == 1. This violates the general contract for Comparable that sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for all x and y that are comparable.

    For some of the subtleties involved in comparing objects and, in particular, testing equality between subclasses, I recommend this excellent article.