A beginner of Java... I'm a bit confused by how to apply double dispatch (and visitor pattern) together with the compareTo method in JAVA.
Example Scenario:
Let's say I have an Animal
interface where I will implement 4 classes: dog, cat, mouse, and bunny.
I want to make sure that if I sort a list of these animals, mouse < bunny < cat < dog.
Wondering how I shall achieve it?
Right now the best I can think of is adding some if-elseif into the compareTo... but this is obviously not dispatchy...
Thanks!
The Animal
interface
package animals;
public interface Animal extends Comparable<Animal> {
void makeSound();
}
The Dog
class
package animals;
public class Dog implements Animal {
@Override public void makeSound() {
System.out.println("wow");
}
@Override public int compareTo(Animal o) {
return 0; // ???? if (o instanceof Cat) {return 1}
}
}
The Answer by Barskov is correct and wise. Probably the best solution for your scenario is to simply add a final static
relativeSize
field to each of the four classes.For fun, let’s look at an alternative approach using an upcoming Java feature.
Sealed classes
The new sealed classes feature being previewed in Java 16 makes this simpler.
When all your allowed concrete types are known at compile-time, you can communicate that fact to the compiler. Mark the class or interface as
sealed
. Addpermits
with the names of implementing/extending classes. The compiler then enforces your claim, verifying that all the designated classes, and only the designated classes, extend/implement the sealed class/interface.We also use default methods. Modern Java allows an interface to have code, as a
default
method to be run if the implementing classes lack that method.Here is some example code.
Notice the keywords:
sealed
,permits
, anddefault
.The concrete classes look like the following. We use
record
here, where the compiler implicitly creates the constructor, getters,equals
&hashCode
, andtoString
. You could just as well use a conventional class.See that in action.
When run.
Do not use mere class comparison as built-in
compareTo
The above code works, and was fun to try. But in real work you should not do this, not build-in such a
compareTo
implementation that merely looks at class only.Read the
Comparable
Javadoc. You will see a discussion of how thecompareTo
method should be consistent withequals
.You would not consider two
Bunny
instances to be equal merely because they are both of the same class. You would also compare their content, the state of their contained data. In the case of arecord
, the default implementation ofequals
(andhashCode
) examines each and every member field. In a conventional class, you would implement something similar, examining anid
field or aname
field or some such.When you do want to compare solely by class, use an external
Comparator
rather than implementingComparable
internally. Something like this:Or use the shorter lambda syntax.