Image

"symmetry" of equals method

I think the idea of equals symmetry should be carefully reviewed. From toposophical perspective, there is a kind of weak point there. And here is a funny example:
public class Bug {
    public static void main(String[] args) {
        java.util.HashMap m1 = new HashMap();
        java.util.HashMap m2 = new HashMap();
        m1.put("myKey", "myValue");
        m2.put("myKey", "myValue");
        if (!m1.equals(m2)) {
            System.err.println(m1 + " must be equal to " + m2);
        }
        if (!m1.values().equals(m2.values())) {
            System.err.println(m1 + ".values() must be equal to " + m2 + ".values()");
        }
    }
}


The second equals() fails, and here's the explanation. values() returns an instance of AbstractCollection, not a List and not a Set. It is not a list, because values in a hashmap are not ordered, and it is not a set because they can repeated. As a result, there is no overriding equals(), and the one from Object applies: plain '==' is used.

Josh Bloch writes in http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5038615:

The collection returned by the values() view is neither a set nor a list,
and cannot usefully override equals.

Here is an excerpt from the spec for Collections.equals:

While the Collection interface adds no stipulations to the general contract for the Object.equals, programmers who implement the Collection interface "directly" (in xxxxx words, create a class that is a Collection but is not a Set or a List) must exercise care if they choose to override the Object.equals. It is not necessary to do so, and the simplest course of action is to rely on Object's implementation, but the implementer may wish to implement a "value comparison" in place of the default "reference comparison." (The List and Set interfaces mandate such value comparisons.)

The general contract for the Object.equals method states that equals must be symmetric (in xxxxx words, a.equals(b) if and only if b.equals(a)). The contracts for List.equals and Set.equals state that lists are only equal to xxxxx lists, and sets to xxxxx sets. Thus, a custom equals method for a collection class that implements neither the List nor Set interface must return false when this collection is compared to any list or set. (By the same logic, it is not possible to write a class that correctly implements both the Set and List interfaces.)

So, the reason behind this absurdity is the requirement of symmetry.

Now, there is one more revelation: using double dispatch for proper implementation of equals()... unfortunately... it is either too late, or it does not cover the whole issue... or maybe we have to choose another language. Haskell?