Saturday 19 December 2009

How to code multiple dispatch in standard Java

This posting continues the theme of multiple dispatch related to the binary method problem and shows a basic double dispatch implementation in standard Java. An appropriate method for computing whether two Shape objects overlap is selected based on runtime argument type. More Shape classes can be added without modifying existing code for shapes (rectangle, square, circle, ect.) already written. Doing it with a drop-through instanceof list is contrasted with low-overhead selection by argument type from an array of methods.


Multiple and predicate dispatching

Single, dynamic dispatching is used by OOP languages to call a designated method based on the runtime type of an object currently at a reference. When the language also features static (compile time) type checking the object's actual type will generally be either the nominal type of the reference or a subclass of that type. In either case, if the object does not have a method matching the static signature of the called method a compile time or runtime error results. For applications written using so called managed-code an exception is thrown at runtime. Using native code languages that do not provide this level of implicit type checking during execution, the error may vary from the method operating on spurious data to uncontrolled execution and possible program termination by the operating system.

In a language that features static type checking and whatever the actual mechanism involved, we can model single dispatching by giving all objects a reference to a table of methods for their class. The compiler stores the table offset of any particular method and when a call against a reference nominally to an object of a particular class is encountered, code is generated to retrieve the entry point for the method from the table. In the model a base class and each subclass has its own table - perhaps not the case as implemented. Overriding a method in a subclass the base-class method is replaced by the subclass method in the subclass' table. If a nominal superclass reference cites a subclass object at runtime, the subclass overridden method entry point is got from the table and this method is executed.

Multiple dispatch extends this method selection mechanism to include the runtime type of method arguments. Extending the model: single dispatching in a statically type-checked environment requires a single method selection table with one dimension. Without static checking an additional dimension is needed related to runtime type. Multiple dispatch requires a table with as many additional dimensions as there are arguments significant to the selection process. The table can be implemented as one per class or one per base class and there can be a table for each multi-method or a single combined table with other tables referenced by its elements.

To prove the concept we will look at implementing a single-method table for a method with a single significant argument in addition to object type. This implementation can be extended to include additional multi-methods by introducing additional tables.

Predicate dispatch extends the multiple dispatch concept by allowing the runtime values of arguments and logical relationships between types and/or values to be significant for method selection. In OOP languages the multiple dispatch and predicate concepts are quite blurred. A reference can cite an object of its nominal class or a subtype and the value of a reference is the object it refers to, one value of which is its type. Ability to include logical selection statements that deal with type and/or other values appears to be the distinguishing factor.


Implementing multiple dispatch

There are a number of possibilities. To do multiple dispatch according to the book we are going to have to use the java.lang.reflect package. If you haven't tried any of this stuff already to get an idea of what it entails you can Google 'Java Reflection' and have a look at some of Sun's postings.

For a more low level view Dennis Sosnoski's articles on IBM developerWorks go down into the world of bytecode and beyond, up to replacing reflection with runtime code generation.

As Dennis shows, using standard reflection is slow, very slow. Reflection may have a use for class initialization but what we are going to look at here is somthing far simpler and more akin to a native code approach - a DIY method table. A great advantage of implementing the table in Java is that it is pretty well guaranteed to be robust but there a number of difficulties to be overcome - methods not being a first-class data type for one.


The binary method problem

A binary method is a method that operates on two or more objects of the same type. It has at least two arguments, perhaps one implicit - the reference to the current instance, the other the same type or to a related class. Suppose we write a base class with a binary method, how can we allow for this method being overridden in a way that works for instances of any subclass including those that have not yet been written and without changing code in the base class?

Using instanceof and typecasting is one way, discussed briefly later, but what is needed is something more akin to overriding methods but related to argument types as well as the instance type. So, how can you override a method substituting covariant arguments that can be used straight off with no typecasts? Without too much difficulty it turns out.

In their paper presenting MultiJava, a Java extension supporting open classes and multiple dispatch, the authors cite the example of a Shape class with subclasses Rectangle, Circle etc. Shape has a binary method intersect(Shape) that returns true if two shapes overlap. I am going to nick this example in a slightly modified form to show how we can implement multiple dispatch in bog standard code. The methodology is kept simple and easy to explain. Perhaps a more generalized solution could be posted later but currently it is worth noting that open classes and multiple dispatch are strongly related to not implementing the Visitor design pattern, enough said!


Using double dispatch

First let's firm up what we are trying to achieve. In Shape nothing is known about rules for checking subclass intersection but in the first subclass, Rectangle say, we can write a method intersect(Rectangle).

Using single dispatching we would override intersect(Shape) from the base class:
    @Override public boolean intersect(Shape argument) {
        if (argument instanceof Rectangle)
            return intersect((Rectangle)argument);
        return argument.intersect(this);
    }
In fact this is programmed double dispatching. If the current class does not have a method to compute intersection with the actual type of the argument, argument.intersect(this) is called to get the result: the method called depends on the argument type. Under this scheme each new subclass includes methods to compute intersection with an instance of itself and all the types that have already been written.

This approach is fine: new shapes can be added as required without recoding any existing shapes and there is no problem with the kind of objections often made to using instanceof selection. The only drawback is that if a class does not implement a method to handle intersection for all the other classes written to date, after an instance of one of those other classes calls its intersect() the subsequent call to argument.intersect(this) gets unterminated recursion and stack overflow. An unavoidable fact of life, but things do not look quite so good when we get up to ten or twelve shapes:
    @Override public boolean intersect(Shape argument) {
        if (argument instanceof Rectangle)
            return intersect((Rectangle)argument);
        if (argument instanceof Square)
            return intersect((Square)argument);
        if (argument instanceof Circle)
            return intersect((Circle)argument);
        if (argument instanceof Triangle)
            return intersect((Triangle)argument);

        //..........................

        return argument.intersect(this);
    }
If this sort of thing is considered an 'anathema to Object Oriented Design' for unary methods why should it be OK for binary methods?

Apart from the call to argument.intersect(this) there is really no element of multiple dispatching in the intersect(Shape) method. Case selection using instanceof or even a more efficient isA construct [here] does not qualify. Multiple dispatching supplies immediate direction of execution to an appropriate method based on argument type. What is needed is a way of calling the required intersect(type) method directly.

Initially we need to map type to the required offset in a table of methods and the least complicated way of doing it is to provide each class with an a property mtOffset() : method table offset - the offset of references to methods with an argument of that class' type in one or more method tables.


The Shape base class

Let's see how this works out in the abstract base class Shape:
public abstract class Shape {

    public static final int MAX_SHAPES = 10;

    protected static int classMTOffset;

    protected static int getOffset() {
        return classMTOffset++;
    }

    protected abstract int mtOffset();
For simplicity the maximum number of shape classes that can be written is the constant MAX_SHAPES. It looks probable that the only way to make the size of the multi-method table fit runtime requirements exactly is to give it a nominal initial size and then expand it if required. For the moment we skip this complication. The method getOffset() is called by each subclass in turn when the class is initialized. getOffset() returns the value of the classMTOffset variable which is also incremented. Subclasses then implement the abstract mtOffset() method to return the value got during initialization.

Now we come to the base type for intersect() methods:
    protected static abstract class Intersect
                                <T extends Shape, S  extends Shape> {

        abstract boolean intersect(T a, S b) ;

     // abstract boolean contains(T a, S b, int tolerance); // possibly
    }
No first-class method type in Java so methods are implemented in nested classes. I am going to call these types Method Classes. There are pros and cons. A big pro is that any number of methods can be defined in a single class. An intersect(Shape) multimethod only requires a single sub-method but we could include a method contains(T, S) returning true if the S type shape fits entirely within T type shape for example, or similar overloaded methods with additional arguments, a tolerance factor maybe.

The characteristic shared by all intersect(T a, S b) methods as overridden in Intersect subclasses is that they all have two arguments that are covariant with Shape. Argument type is constrained by the declaration of the generic parameters to be a Shape or a subclass. Constraint is optional but shows the intention. IntersectRectangle for example is declared as:
     protected static class IntersectRectangle
                            extends Intersect<Rectangle, Rectangle> {

         @Override public boolean intersect(Rectangle t, Rectangle r) {
             //..............
         }
     }
The important factor is that declaring Intersect as a generic class allows intersect(T, S) to be overridden in this way. If for example, we write
    Intersect ref = new IntersectRectangle();
    Boolean b = ref.intersect(t, s);
the method called is intersect(Rectangle t, Rectangle r). Not a typecast in sight but the danger is that any subclass instance can be passed as an argument and passing the wrong kind gets a runtime error. We can design around this problem but a con is that this for the enclosing-class object must be passed as an argument. Of course it gets passed anyway. The dot before a method call can be seen as passing the reference that precedes it.

In this implementation method classes are static nested classes rather than inner classes. Inner classes would be nice: the method body has implicit access to outer-class fields but, because every instance of an inner class carries a reference to the outer-class instance that created it, every outer-class object would need its own method table. Even worse, the execution cost of creating this table is added to the construction cost for every instance. Here a single table is used located in Shape:
 
    protected static Intersect[][] intersectTable =
                                     new Intersect[MAX_SHAPES][];
 
The table has two dimensions one for each argument type. If a method has more arguments subject to multiple dispatch the table is implemented with that number of dimensions. The array of method classes for each class is added to the table when the class is initialized. The index of a method-class array depends on the value of mtOffset() for the class, which results from class initialization order and is arbitrary.

The method-class array for all classes is the same size irrespective of how many Intersect descendants the class implements. If a method to suite a particular combination of arguments is not present the corresponding element will be null, a characteristic used implementing the intersect(Shape) method:
    public boolean intersect(Shape s) {
        if (this == s) return true;

        // get method table offsets for the two types
        int thisMTO = this.mtOffset();
        int sMTO = s.mtOffset();

        // thisMTO indexes the table for the current class of object
        // sMTO the offset for a method to process the argument's type
        if (intersectTable[thisMTO][sMTO] != null) {

            // this object has a method so call it
            return intersectTable[thisMTO][sMTO].intersect(this, s);
        }

        // otherwise call the argument's method
        return intersectTable[sMTO][thisMTO].intersect(s, this);
    }

}  // end of Shape
There is no possibility that the wrong method is called - it is selected programmatically rather than by some scatterbrained programmer ;).

The same strategy shown for instanceof method selection is used to allow additional subclasses to be written: if a null element is found in the current instance's table it does not have a method. It is assumed that the argument implements a suitable method but if it does not a null pointer exception will be thrown rather than stack overflow: still an error but a more traceable result.


Implementing a Rectangle class

MT_OFFSET is initialized by calling Shape.getOffset() and this class' entry in the multy-method table is done by Rectangle.initMT().

public class Rectangle extends Shape {
    public static final int MT_OFFSET = getOffset();
    public static final boolean CLASS_INITIALIZED = initMT();

    private static boolean initMT() {
        intersectTable[MT_OFFSET] = new Intersect[MAX_SHAPES];
        intersectTable[MT_OFFSET][MT_OFFSET] = new IntersectRectangle();
        return true;
    }

    @Override protected int mtOffset() {
        return MT_OFFSET;
    }

    public final int top, left, width, height, right, bottom;

    public Rectangle(int left, int top, int width, int height) {
        this.left = left;
        this.top = top;
        this.width = width;
        this.height = height;
        right = left + width;
        bottom = top + height;
    }

    public static class IntersectRectangle
            extends Intersect<Rectangle, Rectangle> {

        @Override public boolean intersect(Rectangle t, Rectangle r) {

            return (t.right > r.left && t.left < r.right &&
                    t.bottom > r.top && t.top < r.bottom);
        }
    }

    @Override public String toString() {

        return left + ", " + top + ", " + width + ", " + height;
    }
}
A new vector of sub-method references is created at the MT_OFFSET for this class. The class only declares a single sub-method of the intersect(Shape, Shape) multi-method leaving other entries in the vector blank. The implemented method is selected by the inherited intersect(Shape) method when this is a Rectangle and its argument is also a Rectangle.  

Implementing other shapes

Each new Shape implements sub-methods of intersect(Shape, Shape) to compare instances of the class with the instances of that class and other shapes implemented to date. Square declares two sub-methods:
public class Square extends Shape {

    public static final int MT_OFFSET = getOffset();
    public static final boolean CLASS_INITIALIZED = initMT();

    static boolean initMT() {
        intersectTable[MT_OFFSET] = new Intersect[MAX_SHAPES];
        intersectTable[MT_OFFSET][Rectangle.MT_OFFSET] =
            new IntersectRectangle();
        intersectTable[MT_OFFSET][MT_OFFSET] =
            new IntersectSquare();
        return true;
    }

    @Override protected int mtOffset() {
        return MT_OFFSET;
    }

    public final int top, left, side, right, bottom;

    public Square(int left, int top, int side) {
        this.left = left;
        this.top = top;
        this.side = side;
        right = left + side;
        bottom = top + side;
    }

    static class IntersectSquare extends Intersect<Square, Square> {

        @Override public boolean intersect(Square t, Square s) {
            return (t.right > s.left && t.left < s.right &&
                    t.bottom > s.top && t.top < s.bottom);
        }
    }

    static class IntersectRectangle extends Intersect<Square, Rectangle> {

        @Override public boolean intersect(Square t, Rectangle r) {
            return (t.right > r.left && t.left < r.right &&
                    t.bottom > r.top && t.top < r.bottom);
        }
    }

    @Override public String toString() {

        return left + ", " + top + ", " + side;
    }
}
Circle has three intersect() sub-methods:
public class Circle extends Shape {

    public static final int MT_OFFSET = getOffset();
    public static final boolean CLASS_INITIALIZED = initMT();

    static boolean initMT() {
        intersectTable[MT_OFFSET] = new Intersect[MAX_SHAPES];
        intersectTable[MT_OFFSET][Rectangle.MT_OFFSET] =
            new IntersectRectangle();
        intersectTable[MT_OFFSET][Square.MT_OFFSET] =
            new IntersectSquare();
        intersectTable[MT_OFFSET][MT_OFFSET] = new IntersectCircle();
        return true;
    }

    @Override protected int mtOffset() {
        return MT_OFFSET;
    }

    public final int left, top, radius, cx, cy ;

    public Circle(int left, int top, int radius) {
        this.left = left;
        this.top = top;
        this.radius = radius;
        cx = left + radius;
        cy = top + radius;
    }

    static class IntersectCircle extends Intersect<Circle, Circle>{
        @Override public boolean intersect(Circle t, Circle c) {
            int dx = c.cx - t.cx;
            int dy = c.cy - t.cy;
            double distance = Math.sqrt(dx*dx + dy*dy);
            return (distance < t.radius + c.radius);
        }
    }

    static class IntersectRectangle extends Intersect<Circle, Rectangle>{

        @Override public boolean intersect(Circle t, Rectangle r) {
            return t.intRect(r.left, r.top, r.right, r.bottom);
        }
    }

    static class IntersectSquare extends Intersect<Circle, Square>{

        @Override public boolean intersect(Circle t, Square s) {
            return t.intRect(s.left, s.top, s.right, s.bottom);
        }
    }

    @Override public String toString() {
        return left + ", " + top + ", " + radius;
    }

    private int distance(int x, int y) {
        int dx = cx - x;
        int dy = cy - y;
        return (int)Math.sqrt(dx*dx + dy*dy);
    }

    private boolean intRect(int left, int top, int right, int bottom) {
        int lr = left - radius;
        int rr = right + radius;
        int tr = top - radius;
        int br = bottom + radius;
        if (cx <= lr || cx >= rr || cy <= tr || cy >= br)
            return false;
        if (cx <= left) {
            if (cy <= top)
                return distance(left, top) < radius;
            if (cy >= bottom)
                return distance(left, bottom) < radius;
        } else if (cx >= right) {
            if (cy <= top)
                return distance(right, top) < radius;
            if (cy >= bottom)
                return distance(right, bottom) < radius;
        }
        return true;
    }
} 

Testing it out

First sketch some shapes and modify the test function from the implementing equals() article then code main to compare the shapes.

By the way, I make no claims that the intersect algorithms are efficient or even correct: Let's see how this works out in the abstract base class Shape:
public class TestIntersect {

    public static void interPrint(Shape a, Shape b) {
        String ac = a.getClass().getCanonicalName();
        if (ac == null)
            ac = "?";
        else
            ac = ac.substring(ac.indexOf('.') + 1);
        String bc = b.getClass().getCanonicalName();
        if (bc == null)
            bc = "?";
        else
            bc = bc.substring(bc.indexOf('.') + 1);
        System.out.println(ac + " " + a.toString() + " a intersect() " +
               bc + " " + b.toString() + " b is " + a.intersect(b));
        System.out.println("aoff: " + a.mtOffset() + " boff: " +
           b.mtOffset()+ ", " + bc + " b intersect() " + ac + " a is " +
           b.intersect(a) );
    }

    public static void main(String[] args) {
        Square square1 = new Square(10, 10, 40);
        Circle circle1 = new Circle(30, 30, 20);
        Rectangle rect1 = new Rectangle(40, 40, 20, 80);
        interPrint(square1, circle1);
        interPrint(circle1, rect1);
        interPrint(square1, rect1);
        System.out.println();
        Square square2 = new Square(60, 120, 40);
        Circle circle2 = new Circle(94, 85, 20);
        Rectangle rect2 = new Rectangle(103, 5, 20, 80);
        interPrint(square2, rect1);
        interPrint(square2, circle2);
        interPrint(circle2, rect2);
        interPrint(square2, rect2);
    }
}
Works O.K. Note that the Square's mtOffset() is zero because it is initialized first. Changing instance construction order in main() will change these values but functionality is not affected.  

Square 10, 10, 40 a intersect() Circle 30, 30, 20 b is true  
aoff: 0, boff: 2. Circle intersect() Square is true  
Circle 30, 30, 20 a intersect() Rectangle 40, 40, 20, 80 b is true  
aoff: 2, boff: 1. Rectangle intersect() Circle is true  
Square 10, 10, 40 a intersect() Rectangle 40, 40, 20, 80 b is true 
aoff: 0, boff: 1. Rectangle intersect() Square is true
   
Square 60, 120, 40 a intersect() Rectangle 40, 40, 20, 80 b is false  
aoff: 0, boff: 1. Rectangle intersect() Square is false 
Square 60, 120, 40 a intersect() Circle 94, 85, 20 b is false  
aoff: 0, boff: 2. Circle intersect() Square is false  
Circle 94, 85, 20 a intersect() Rectangle 103, 5, 20, 80 b is false  
aoff: 2, boff: 1. Rectangle intersect() Circle is false  
Square 60, 120, 40 a intersect() Rectangle 103, 5, 20, 80 b is false 
aoff: 0, boff: 1. Rectangle intersect() Square is false  

Summary
 
Generic nested classes provide a way of declaring methods capable of multiple dispatch selection in standard Java. Without performance testing and as-is the technique looks faster than selection from a list of isA statements.

Thursday 10 December 2009

Test

Decided on this syntax highlighter. Needed some typeface tweaking but has the great advantage that text can be copied without the line numbers if anyone would like to try some of my crazy code.

public class Customer {

    private String firstName;
    private String lastName;
    private Date birthday;
    private String street;
    private String city;
    private String zipcode;

    // follows the Eclipse generated result - two nulls are equal, but a
    // couple of trivial static functions can take out many lines of code
    public static boolean eqFields (Object a, Object b) {
        if (a != null)
            return a.equals(b);
        else
            return (b == null);
    }

    protected boolean equalFields(Customer rhs) {
        return eqFields(firstName, rhs.firstName) &&
               eqFields(lastName, rhs.lastName) &&
               eqFields(birthday, rhs.birthday) &&
               eqFields(city, rhs.city) &&
               eqFields(zipcode, rhs.zipcode);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        return (obj instanceof CustomerOrg &&
                ((Customer)obj).equalFields(this));
    }

    public static int hash(Object obj) {
        return ((obj == null) ? 0 : obj.hashCode());
    }

    @Override
    public int hashCode() {
        int result = 21 + hash(birthday);
        result = result * 31 + hash(city);
        result = result * 31 + hash(firstName);
        result = result * 31 + hash(lastName);
        result = result * 31 + hash(street);
        return result * 31 + hash(zipcode);
    }
}

Sunday 6 December 2009

The dangers of overloading

Posted the equals(Object) solution from my last blog on the artima discussion for this topic. As a contributor demonstrated, if the protected field comparison methods are called from outside an instance some very strange results can be produced. The answer is of course that protected methods should not be called from outside a class even if Java's same-package rule permits access. Default access gives plenty of scope for any required kludge without ignoring this last vestige of encapsulation.

These strange results should certainly discourage giving these methods public access or overloading an equals() with the class name, as seen in at least one published implementation. As things stand it is still quite possible to get the protected methods to return a symmetrical false when comparing the same object. All down to overloading, substitution and the aforementioned weak encapsulation but worth a look.

The first thing to do is write a Point base and a ColoredPoint subclass, as in the artima article, but use the new equals() implementation rather canEqual. In the base-class field comparison is factored of to fEquals(Point) and equals(Object) calls the received object's version of fEquals.

public class Point {

private final int x;
private final int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

// accessors are optional for this discussion

@Override public boolean equals(Object obj) {
return (obj instanceof Point &&
((Point)obj).fEquals(this) );
}

protected boolean fEquals(Point obj) {
return (this.x == obj.x && this.y == obj.y);
}


@Override public int hashCode() {
return (41 * (41 + getX()) + getY());
}
}

Point can now be subclassed without overriding equals() and instances of all types will provide symmetry when compared for equality. ColouredPoint overrides equals() and overrides fEquals(Point) to return false - the required return comparing a ColoredPoint with a Point.

public class ColoredPoint extends Point {

private final Color color;

public ColoredPoint(int x, int y, Color color) {
super(x, y);
this.color = color;
}

// ....................

@Override public boolean equals(Object obj) {
return (obj instanceof ColoredPoint &&
((ColoredPoint)obj).fEquals(this));
}

@Override protected boolean fEquals(Point obj) {
return false;
}

protected boolean fEquals(ColoredPoint obj) {
return (this.color.equals(obj.color) && super.fEquals(obj));
}

@Override public int hashCode() {
return (41 * super.hashCode() + color.hashCode());
}
}

ColoredPoint gets a fEquals(ColoredPoint) overloading and again equals() calls the received object's version. Using this technique we can write as many Point and ColoredPoint subclasses as we like that do or do not override equals().

To show two references to the same object giving a symmetrical false the same ColoredPoint is assigned to a Point reference and a ColoredPoint reference:

public static void main(String[] args) {
Point p1 = new Point(1, 1);
ColoredPoint cp1 = new ColoredPoint(1, 1, Color.PINK);
Point p2 = cp1;

First do a check that equals(Object) is working O.K. Of the following the first two pairs should return symmetric false comparing a Point with a ColoredPoint. The last pair should get a symmetric true comparing the same ColoredPoint. A same-object check has not been put in equals so the full field comparison is done:

System.out.println("p1.equals(p2) is " + (p1.equals(p2)));
System.out.println("p2.equals(p1) is " + (p2.equals(p1)));
System.out.println("p1.equals(cp1) is " + (p1.equals(cp1)));
System.out.println("cp1.equals(p1) is " + cp1.equals(p1)));
System.out.println("p2.equals(cp1) is " + (p2.equals(cp1)));
System.out.println("cp1.equals(p2) is " + (cp1.equals(p2))+ "\n");

Output is:
p1.equals(p2) is false
p2.equals(p1) is false
p1.equals(cp1) is false
cp1.equals(p1) is false
p2.equals(cp1) is true
cp1.equals(p2) is true


Everything works as expected but now the overloaded fEquals() methods are tested:

System.out.println("p1.fEquals(p2) is " + (p1.fEquals(p2)));
System.out.println("p2.fEquals(p1) is " + (p2.fEquals(p1)) + "\n");
The p1 reference is a Point type that references a Point so Point's fEquals is called and the x, y fields are compared: true expected. The ClouredPoint argument referenced by p2 at runtime has no effect at compile or runtime. Runtime argument type never has any effect on which method gets called.

For the second comparison the p2 reference is also a Point type. The compile time call is to fEquals(Point) but p2 references a ColoredPoint and its overriden version of fEquals(Point) is called at runtime: false expected, this method always returns false.

Output is:
p1.fEquals(p2) is true
p2.fEquals(p1) is false
The next two comparisons behave in a similar way:

System.out.println("p1.fEquals(cp1) is " + (p1.fEquals(cp1)));
System.out.println("cp1.fEquals(p1) is " + (cp1.fEquals(p1)) + "\n");
p1.fEquals(cp1) behavior is the same as p1.fEquals(p2), true expected. In cp1.fEquals(p1) gets a call to ColoredPoint's version of fEquals(Point): always false.

Output is:
p1.fEquals(cp1) is true
cp1.fEquals(p1) is false
The second result is obvious but now the references to the same ColoredPoint are compared:

System.out.println("p2.fEquals(cp1) is " + (p2.fEquals(cp1)));
System.out.println("cp1.fEquals(p2) is " + (cp1.fEquals(p2)));
}
gets:
p2.fEquals(cp1) is false
cp1.fEquals(p2) is false
cp1.fEquals(p2) is false is particularly unintuitive unless we remember that the runtime type of an argument has no effect on which method gets called - ever, unless said method is an exception handler that is.


The binary method problem

Elsewhere multiple and predicate dispatching add another dimension to overridden methods by making the argument type/s direct execution to an appropriate method. A Java extension that supports predicate dispatching is [here] with a covering paper [pdf].

One thing that multiple dispatching is good at is solving the binary method problem but at least a limited solution is possible in standard Java. Suppose we write a base class that has a method with an argument referencing an instance of the class or a same type array argument: a binary method. How can we allow for this method being overridden so that it works for instances and arguments of any subclass including those that have not yet been written and without changing code in the base class? The equals() implementation is a special case solution to the binary method problem using programmed double dispatching.


Diverse identifiers

Overloading is simply a convenience that saves having to think up a new method name. It forms the basis of operator overloading but may not be appropriate when methods are subject to being overridden in a subclass. Overloading adds nothing to functionality and as shown above can get unwanted side effects when an overloaded method is overridden. Maybe we need to consider whether this kind of polymorphism is generally helpful or whether it only adds ambiguity in some and perhaps in many cases - not for the compiler of course but when trying to interpret someone else's code or our own at a later time.

There is no reason why fEquals should not be given different identifiers in different classes, pointEquals(Point) and coloredPointEquals(ColoredPoint) for example. On the other hand doing this gets another common OOP problem: linkedColoredPointEquals(something) - huge identifiers that wrap code onto the next line. When implementing equals() there is never any scope for calling the protected method against a reference with an inappropriate nominal type. Perhaps all that is needed to provide better clarity is a more explicit overloaded method name, equalFields(...), for example.