Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

Having scoured the web and found little helpful information regarding the implementation of an overridable compareTo() method, I determined myself to find a solution. In another thread I was pointed to some information pertaining to an overridable equals() method, however compareTo() has some pretty unique issues which must be overcome (namely, how to deal with objects of a related supertype, but each existing in a separate hierarchical branch). I've considered many possible scenarios and believe that I've implemented a type which allows for the modification of relational behavior by subtypes, however I'd like external verification that I'm not breaking the compareTo() contract.

public abstract class RelationalObject
implements Comparable<RelationalObject>
{
    /*
     * Compares two RelationalObjects for semantic ordering.
     *
     * @param other The RelationalObject to be compared.
     *
     * @return An int value representing the semantic relationship between the
     *         two RelationalObjects. A value of 0 is returned if the two
     *         objects are determined to be equal. A negative value is
     *         returned if "this" object is determined to have a
     *         lower semantic ordering than the "other" object. A positive
     *         value is returned if "this" object is determined to have a
     *         highter semantic ordering than the "other" object.
     */
    public final int compareTo(RelationalObject other)
    throws ClassCastException, NullPointerException
    {
        if (other == null)
            throw new NullPointerException("other: Cannot be null.");

        int relation = 0;

        if (!this.equals(other))
        {
            if (this.getClass().isAssignableFrom(other.getClass()))
            {
                if (this.getClass() == other.getClass())
                    relation = this.compareToExactType(other);
                else
                    /*
                     * Defer comparison to subtype, thereby allowing an
                     * optional override of compareToSuperType().
                     */
                    relation = -1 * other.compareTo(this);
            }
            else
            {
                if (other.getClass().isInstance(this))
                    relation = this.compareToSuperType(other);
                else
                    relation = this.compareToForeignType(other);
            }
        }

        return relation;
    }

    /*
     * Compares two RelationalObjects with the exact same class type for
     * semantic ordering. The comparison may be based upon any of the class
     * state elements so long as the compareTo() method contract is not
     * broken.
     *
     * @param exact The RelationalObject with exactly matching type to be
     *              compared.
     *
     * @return An int value representing the semantic relationship between the
     *         two RelationalObjects.
     */
    protected abstract int compareToExactType(RelationalObject exact);

    /*
     * Compares two RelationalObjects not within the same hierarchical branch
     * for semantic ordering. The comparison may be based upon only those
     * state elements common to both objects (i.e. A comparison must be made
     * between each element and the pair's common ancestor). Should the two
     * results be equal, a ClassCastException must be thrown as the objects do
     * not contain enough distinct information to be further compared.
     *
     * As only one implementation exists, this should be finalized by the first
     * derivative of RelationalObject.
     *
     * @param foreign The RelationalObject from a foreign hierarchical branch
     *                to be compared.
     *
     * @return An int value representing the semantic relationship between the
     *         two RelationalObjects.
     */
    protected abstract int compareToForeignType(RelationalObject foreign);

    /*
     * Compares two RelationalObjects within the same class hierarchical
     * branch for semantic ordering. The comparison may be based upon any of
     * the class state elements so long as the compareTo() method contract is
     * not broken.
     *
     * @param upper The RelationalObject within the same heirarchical branch
     *              and with lesser definition to be compared.
     *
     * @return An int value representing the semantic relationship between the
     *         two RelationalObjects.
     */
    protected abstract int compareToSuperType(RelationalObject upper);
}
share|improve this question
add comment

1 Answer

Nice question! Some thoughts about it:

If B and C are subclasses of A (in separate hierarchical branches) they usually should not know about each other even in the compareToExactType method. It leads to tight coupling which we try to avoid in OOP.

Here the other.compareTo(this) and similar calls seems hard to follow, though ones which would not be the easiest to debug, maintain or to figure out what is the order that it provides in a complex case.

I'd try to use a separate Comparator class which knows all the necessary information about A, B, C and the ordering. You may could use something like that was mentioned in my former answer in the Multi-tiered sorting using custom IComparer question.

share|improve this answer
    
Thanks for the suggestions. I'll give the Comparator idea some thought, however I must ensure that subclasses are able to cleanly (without modification) inherit the relational behavior of their parent while also being able to change this behavior without breaking the compareTo() contract. –  blf Aug 1 '12 at 16:20
    
I can agree that the various sub-comparisons are at times a little difficult to follow, however I found this to be necessary in order to allow clean (no modification) inheritance while at the same time allowing for modifiable inheritance. (i.e.: -1 * other.compareTo(this) says to defer all actual comparisons to the subtype which should be able to make a more informed decision, while other.compareToForeignType(this) is basically a clever way of allowing the other object to override the compareToForeignType() method so that this instance will not automatically throw ClassCastException. –  blf Aug 1 '12 at 16:26
    
I'm glad you spurred me to take another look. I was considering the Foreign Type comparison somewhat incorrectly. There will only ever be one such comparison to be made. I've updated the code segment and comments to reflect accordingly. –  blf Aug 2 '12 at 5:18
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.