Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implementation of equals(): compare against implemented interface or implementing class?

I'ver been wondering how to best implement equals() for a family of classes that all implement the same interface (and the client is supposed to work only with said interface and never to know about implementing classes).

I haven't cooked up my own concrete example, but there are two examples in the JDK - java.lang.Number and java.lang.CharSequence that illustrate the decision:

boolean b1 = new Byte(0).equals( new Integer(0) ) );

or with CharSequence

boolean b2 = "".equals(new StringBuilder());

Would you ideally want those to evaluate to true or not? Both types do implement the same datatype interface, and as a client working with Numbers (resp. CharSequences) instances I would have an easier life if equals would compare the interface types instead of the implementing types.

Now this is not an ideal example, as the JDK exposes the implementing types to the public, but suppose we had not have to uphold compatibility with what is already there - from a designers point of view: Should equals check against the interface or is it better the way it is, checking against the implementation?


Note: I understand that checking for equality against an interface can be very hard to actually implement properly in practice and its made even more tricky since equal interfaces also need to return the same hashCode(). But those are only obstacles in implementation, take for example CharSequence, although the interface is pretty small, everything required for equality checks is present whithout revealing the internal structure of the implementation (so it is principally possible to implement properly, even without knowing about future implementations in advance). But I am more interested in the design aspect, not on how to actually implement it. I wouldn't decide solely based on how hard something is to implement.

like image 617
Durandal Avatar asked Aug 12 '11 16:08

Durandal


1 Answers

Define an abstract class that implements your interface and defines final equals()/hashCode() methods and have your customers extend that instead:

public interface Somethingable {
    public void something();
}

public abstract class AbstractSomethingable implements Somethingable {
    public final boolean equals(Object obj) {
        // your consistent implementation
    }

    public final int hashCode() {
        // your consistent implementation
    }
}

Notice that by making your class abstract, you can implements the interface without defining the interface's methods.

Your customers still have to implement the something() method, but all their instances will use your code for equals()/hashCode() (because you've made those methods final).

The difference to your customers is:

  • Using the extends keyword instead of the implements keyword (minor)
  • Not being able to extend some other class of their choosing to use your API (could be minor, could be major - if it's acceptable then go for it)
like image 180
Bohemian Avatar answered Nov 07 '22 21:11

Bohemian