5

Let's say we have 4 classes A, B, C, D where: A is a superclass of both B and C and C is a superclass of D. I suppose, in a diagram, it should look like this:

   A
 /   \
B     C
       \
        D

Moreover, the class A implements the interface

Comparable<A> 

and also implements a method

public int compareTo(A another)

I'd like to be able to compare every pair of objects x, y such that they are instances of classes from the set {A, B, C, D}. Naturally, I should implement a compareTo method in each of these classes but by default I'm doing this with one sided effect, i.e. defining how to compare object c from C with b from B doesn't tell anything about how to compare b with c. Now, I could write a lot of code and handle all of those cases (this also means I would have to use 'isInstance' method to handle these, right?) but then every two classes have to be 'aware of' existence of others. So if I wanted to add another class, E, such that D is a superclass of E, I would have to serve all cases in the new class E and moreover I'd have to change something in classes A, B, C, D in order to 'be aware of E'.

So I'm struggling to find an elegant solution to it, i.e. one that doesn't entail changing a lot while adding a new class to the hierarchy. How can I achieve this?

Thank you in advance for help.

  • 2
    I'm not sure I understand the problem. If you are comparing two As why do you need to know if they are B, C or Ds? If you want to get different results depending on the subtypes you will have to deal with the combinatorial explosion of combinations when adding types. Do you want some rule like comparing differing types as the most specific common base type? – Sarien Apr 3 '13 at 9:26
  • @Sarien - for instance because I'd like to have a bag of objects that can be A, B, C or D and then sort it properly. And yes, something like finding the lowest common ancestor in the tree (diagram) of inheritance is something I'm striving for. Moreover, the ability to add new classes (like E from description) without changing the code in A,B,C,D is a crucial point for me. – socumbersome Apr 3 '13 at 17:27
4

You could write a general compareTo method in class A, which uses a key computed by a method compareKey which is also in A, but can be overridden by each subclass to make as fine a distinction as it wants. If you make it return a float, you can keep subdividing the comparison criterion whenever a new level of inheriting classes is added (within reason, obviously).

  • Suppose you have a double field in class A already - how would you map this to a float? – scarfridge Apr 3 '13 at 8:54
  • 1
    @scarfridge Why would you want to map it to a float? If A has a double and you are in D comparing it to another thing that is of type A (may be a D, may be a B) you are comparing it based on what is in A - not what is in B or D. If there is something common between B and D that makes sense for comparing, it should be in A. – user40980 Apr 3 '13 at 13:51
  • @Kilian Foth - I don't quite follow your idea. Will the anti-symmetry property hold in your solution? And I don't see how to maintain an assertion that we always look for a lowest common ancestor class and use its compareTo. Could you give a little example of code, please? – socumbersome Apr 3 '13 at 17:42
  • @socumbersome: The assertion holds because there is only one compareTo method. public int compareTo(A other) {return this.compareKey().compareTo(other.compareKey());}. This holds symmetry if the compareKey() method return values do. And those can befloat, or String, or anything else. – Mooing Duck Dec 29 '17 at 8:29
1

I think what you want is sort of multiple dispatch which is ususally implemented in Java using the Visitor-Pattern but you would have to change the code when adding new classes.

Since you don't really need full-blown multiple dispatch I think the following might be a good solution. Write your compareTo methods like this: (This is the one in TestB)

@Override
public int compareTo(TestA o) {
    if(o instanceof TestB) {
        // add your code instead
        System.out.println("Compare Bs");
        return 0;
    } else {
        return super.compareTo(o);
    }
}

Since the call to x.compareTo is resolved at run-time you always run the compareTo function of the most specific class. By checking if the other argument is instanceof the current class you can detect when you have to move up the hierarchy which you can do calling super.compareTo.

Let me know if you have any further questions. :)

Edit: I assumed what you wanted to do was compare as the most specific common ancestor as per your comment on the original question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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