Barry Fruitman
Barry Fruitman

Reputation: 12666

Comparing Java subclasses

My app may compare instances of any two sub-classes of the same abstract parent class. I want them compared as follows:

  1. If they are different sub-classes, the parent class should do the comparison.
  2. If they are the same sub-class, the sub-class should do the comparison.

The classes will be compared by a TreeMap, so I have a choice of using a Comparator, or implementing Comparable (or both?).

I can think of several ways to do this, but they are all a bit messy and error-prone. Is there an elegant solution?

Thanks in advance...

Upvotes: 2

Views: 5147

Answers (5)

Barry Fruitman
Barry Fruitman

Reputation: 12666

This is how I'm doing it now. I think this is as elegant as it's going to get:

public abstract class ParentClass implements Comparable<ParentClass> {
    // compareTo(ParentClass) is implicitly abstract
}


public class SubClass1 extends ParentClass /* Comparable<> is implicitly implemented */ {
    @Override
    public int compareTo(ParentClass another) {
        SubClass1 subAnother = (SubClass1) another;
        return /* result of sub-class comparison */;
    }
}


public class MyComparator implements Comparator<ParentClass> {
    @Override
    public int compare(ParentClass lhs, ParentClass rhs) {

        // Are lhs and rhs instances of the same sub-class?
        if(!lhs.getClass().equals(rhs.getClass()))
            // They are different. Use parent class comparison.
            return /* result of parent class comparison */;

        // They are the same. Use sub-class comparison.
        return lhs.compareTo(rhs);
    }
}

Then I just pass an instance of MyComparator to the TreeMap. It handles the different-sub-class comparison, or passes the comparison to the sub-class if they're the same.

What I don't like is that SubClass1.compareTo() will throw a ClassCastException if another is an instance of another sub-class. However I don't have to worry about this as long as I make sure the sub-classes are only compared by MyComparator.

Upvotes: 0

Attila
Attila

Reputation: 28762

You could try

// Parent:
@Override
public final int compareTo(Parent other)
{
  if (getClass() == other.getClasss()) {
    // same type -> pass it to subclass implementation
    return this.subCompare(other)
  }

  // different type -> do the comparison here based on Parent's logic
  // ...
}

protected int subCompare(Parent other)
{
  // this should not be called directly
  return 0; // could throw an exception here too
}

// Derived1:
@Override
protected int subCompare(Parent other)
{
  // this method is only called from Parent
  Derived1 other1 = (Derived1) other;
  // do the comparison based on Derived1's logic
}

Similarly for other derived classes

Upvotes: 2

Kasper van den Berg
Kasper van den Berg

Reputation: 9526

Should the class hierarchy be extendible or not?
=> If it is not extendible (or rarely will be extended) you could implement a Comperator so that all comparing code is in one place.

Is there more than one way to order the objects? => If so, then you 'll have to use a comperator for each order.

compareTo(other) (or compare(o1, o2)) is invoked from three situations:

  1. this instanceof other.class (i.e. o1 instanceof o2.class):
    • this.class should compare this and o2 since it has more information than other.class;
  2. other instanceof this.class (i.e. o2 instanceof o1.class):
    • other.class should compare other and this and this.class.compareTo should flip the result and return it.
  3. exist first common ancestor P such that this instanceof P && other instanceof P:
    • P should compare this and other: call super.compareTo(other) recursively and return the result.

Upvotes: 0

Eugene Kuleshov
Eugene Kuleshov

Reputation: 31795

It is going to be error prone either way. You could do something like this in your subclass:

class Subclass1 ... {

public boolean equals(Object o) {
  if(o instanceof Subclass1) {
    return super.equals(o);
  }

  ... compare subclasses
}

}

Upvotes: 0

Cratylus
Cratylus

Reputation: 54084

This is not a direct answer to your question but:
I believe that what you are doing is error prone and problematic.
The equality test imposed by the compareTo method should generally return the same results as the equals method. I.e. the ordering imposed by the compareTo method is should be consistent with equals.
If this contract is violated you could run into problems with sorted collection (which you are using).
Why I am saying this:
The fact that you want to delegate comparison to the parent class in certain cases indicates to me that you have added a value component in the subclass.
If you did this then there is no way possible to preserve the equals contract and you could run in problems with sorted container (see above)

Upvotes: 0

Related Questions