After reading up on (again, should have done this a long time ago) implementing equals and hashcode correctly i came to these conclusions,that works for me:
If pre JDK 7: Prefer using Apache commons equalsbuilder and hashcodebuilder. (or Guava). Their javadocs contain examples of how to use them in good way.
If JDK 7++: Use the new Objects utility class
But, If writing for hibernate some special requistes appear (see sources farther down) Amongst them are the recommended usage of instanceof instead of getClass, due to hibernate creating proxys of subclasses that are lazy-loaded.
But as i understand, if doing this another potential problem occurs: The reason to use getClass is to ensure the symmetric property of the equals contract. JavaDocs:
*It is symmetric: for any non-null reference values x and y, x.equals(y)
should return true if and only if y.equals(x) returns true.*
And by using instanceof, it's possible to not be symmetric. Example: B extends A. A's equals does an instanceof check of A. B's equals does an instanceof check of B. Give A a and B b:
a.equals(b) --> true b.equals(a) --> false
How to implement equals with hibernate without risking losing the symmetric property? It seems i'm not safe when using getClass, and i'm not safe when using instanceof?
Is the answer to never add significant members to subclasses, and then be safe in using instanceof (for hibernate that is)?
Sources i read:
What issues should be considered when overriding equals and hashCode in Java?
Items 7 and 8 in Josh Blochs excellent book "Effective Java", http://web.archive.org/web/20110622072109/http://java.sun.com/developer/Books/effectivejava/Chapter3.pdf
About Java 7: http://www.javacodegeeks.com/2012/11/guavas-objects-class-equals-hashcode-and-tostring.html
If you want to compare the classes of both objects in the equals method you can use Hibernate.getClass on both objects before comparing them. Then you don't run into trouble e.g. when comparing an object and a hibernate proxy for an object.
After rading up some more i summarize this question with:
- Use instanceof and you can never add significant members to subclasses.( There is no way to extend an instantiable class and add a value component while preserving the equals contract (Bloch)
- Use getClass and you violate the Liskov substitution principle
Langers says http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals.html
- The instanceof test is correct only for final classes or if at least method equals() is final in a superclass. The latter essentially implies that no subclass must extend the superclass's state, but can only add functionality or fields that are irrelevant for the object's state and behavior, such as transient or static fields.
Implementations using the getClass() test on the other hand always comply to the equals() contract; they are correct and robust. They are, however, semantically very different from implementations that use the instanceof test. Implementations using getClass() do not allow comparison of sub- with superclass objects, not even when the subclass does not add any fields and would not even want to override equals() . Such a "trivial" class extension would for instance be the addition of a debug-print method in a subclass defined for exactly this "trivial" purpose. If the superclass prohibits mixed-type comparison via the getClass() check, then the trivial extension would not be comparable to its superclass. Whether or not this is a problem fully depends on the semantics of the class and the purpose of the extension.
Summary - use instanceof with final for equals avoids breaking symmetry, and avoids the hibernate "proxy" problem.
Links
This is an interesting question - implementing equals
as an equivalence relation is not trivial when inheritance is involved. See this in-depth post by Martin Odersky et al. about implementing object equality.
来源:https://stackoverflow.com/questions/14802422/how-to-implement-equals-with-hibernate-without-risking-losing-the-symmetric-prop