Implementing equals (): comparing with an implemented interface or an implementing class?

I was wondering how best to implement equals () for a family of classes that implement the same interface (and the client should only work with the specified interface and never know about the implementation of the classes).

I have not prepared my own specific example, but there are two examples in the JDK - java.lang.Number and java.lang.CharSequence, which illustrate the solution:

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

or with CharSequence

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

Do you ideally want those who value this to be true or not? Both types implement the same data type interface, and as a client working with Numbers instances (CharSequences respectively), it would be easier for me if equals compared interface types instead of implementation types.

Now this is not a perfect example, since the JDK reveals implementation types to the public, but suppose that we do not need to maintain compatibility with what already exists, from the point of view of designers: it should be checked for compliance with the interface or better, the way it is checking implementation?


Note. I understand that checking for equality with respect to an interface can be very difficult to actually implement in practice, and make it even more complex, since equal interfaces must also return the same hashCode (). But these are only obstacles in the implementation, for example, CharSequence, although the interface is quite small, everything that is required for equality checks is present without revealing the internal structure of the implementation (therefore, it is fundamentally possible to correctly implement it, without even knowing about the future implementation in advance). But I'm more interested in design, not how to implement it. I would not decide, based only on how difficult it is to implement something.

+8
java equals design interface
source share
6 answers

I would suggest that "similar" objects would not be equal - for example, I did not expect Integer (1) to pass equal to (Long (1)). I can imagine situations where this would be reasonable, but since jdk should be a general purpose API, you cannot make the assumption that this will always be correct.

If you have any custom objects where this is reasonable, I think it's nice to implement the extended definition of equals if you

  • sure that you do not have some cases when you really need more specific equality (i.e. it requires the same classes)
  • document this very clearly.
  • make sure that hashcode is consistent with your new peers.
+3
source share

Define an abstract class that implements your interface, and define final equals () / hashCode () methods, and instead, your clients will extend this:

 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 } } 

Note that by creating your abstract class, you can implements interface without defining interface methods.

Your clients should still implement the something() method, but all their instances will use your code for equals () / hashCode () (because you made these methods final ).

The difference between your customers:

  • Using the extends instead of the implements keyword (minor)
  • The inability to extend any other class of your choice to use your API (may be insignificant, may be the main one - if this is acceptable, then go)
+5
source share

For what it's worth, I would probably implement an implementation equivalence implementation (note - don't forget to implement hashCode ...). The level of equality equals () imposes a rather heavy burden on interface developers - who may or may not know about the special requirement.

Often the implementation level works fine, since your client deals with only one implementation (i.e.MyNumberProcessor can work with any number, but almost one instance of it should be processed only by Long and, possibly, another Double). Generics are a great way to make sure this is happening.

In the rare case when it matters, I would probably design a client to allow comparator injection or - when it is not available - to encapsulate my numbers in VarTypeNumber.

0
source share

I would consider any implementation of equals that returns true for two objects that do not have the same specific type, which is extremely surprising. If you work in a box where you know during compilation all possible implementations of the interface, you can make equal, which makes sense only with the help of the interface methods, but this is not a reality for the API code / framework.

You can’t even be sure that no one is going to write an implementation of an interface that changes its internal state when you call methods that you used to implement equals! Talk about obfuscation, equality checking returning true and invalid in the process?

-

This is what I understood as a question "checking equality against interface":

 public interface Car { int speedKMH(); String directionCardinal(); } public class BoringCorrolla implements Car { private int speed; private String directionCardinal; public int speedKMH() { return speed; } public String directionCardinal() { return directionCardinal; } @Override public boolean equals(Object obj) { if (obj isntanceof Car) { Car other = (Car) obj; return (other.speedKMH() == speedKMH() && other.directionCardinal().equals(directionCardinal()); } } } public class CRAZYTAXI implements Car, RandomCar { public int speedKMH() { return randomSpeed(); } public String directionCardinal() { return randomDirection();} } 
0
source share

I would try adding another Equals method to my interface. How about this:

 assertFalse(new Integer(0).equals(new Byte(0))); // pass assertTrue(new Integer(0).valueEquals(new Byte(0))); // hypothetical pass 

This does not create unexpected behavior (different types are equal), but allows you to open the possibility for checking equal values.

This discusses a somewhat related topic in efficient java, which discusses peers with instanceof and getClass. However, I do not remember the item number.

0
source share

You can define equality between different classes.

In your case, the exact equality algorithm must be specified by the interface, so any class that implements the interface must follow it. Even better, since the algorithm depends only on the information open by a personal word, just implement it, so subclasses can simply borrow it.

 interface Foo class Util static int hashCode(Foo foo){ ... } static boolean equal(Foo a, Foo b){ ... } static boolean equal(Foo a, Object b) return (b instanceof Foo) && equal(a, (Foo)b); class FooX implements Foo int hashCode() return Util.hashCode(this); boolean equals(Object that) return Util.equal(this, that); 
0
source share

All Articles