如何实现与hibernate相等而不会失去对称属性的风险?

在阅读之后(再次,应该已经做了很久以前)正确实现了equals和hashcode我得出了这些结论,这对我有用:

如果在JDK 7之前 :首选使用Apache commons equalsbuilder和hashcodebuilder。 (或番石榴)。 他们的javadoc包含如何以良好的方式使用它们的示例。

如果是JDK 7 ++ :使用新的Objects实用程序类

但是,如果为hibernate写一些特殊的请求出现(参见更远的源代码)其中推荐使用instanceof而不是getClass ,因为hibernate创建了延迟加载的子类的代理。

但据我所知,如果这样做,另一个潜在的问题就出现了:使用getClass的原因是为了确保equals合约的对称属性。 JavaDoc中:

*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.* 

通过使用instanceof,它可能不是对称的。 示例:B扩展A. A的等于A的检查实例.B的等于B的实例检查。给A a和B b:

a.equals(b) – > true b.equals(a) – > false

如何实现与hibernate相等而不会失去对称属性的风险? 使用getClass时我似乎不安全,使用instanceof时我不安全?

答案是永远不要将重要成员添加到子类,然后使用instanceof是安全的(对于hibernate)?

我读到的消息来源:

在Java中覆盖equals和hashCode时应该考虑哪些问题?

Josh Blochs的第7和第8项优秀书籍“Effective Java”, http: //web.archive.org/web/20110622072109/http://java.sun.com/developer/Books/effectivejava/Chapter3.pdf

关于Java 7: http : //www.javacodegeeks.com/2012/11/guavas-objects-class-equals-hashcode-and-tostring.html

如果要在equals方法中比较两个对象的类,可以在比较它们之前在两个对象上使用Hibernate.getClass 。 然后你不会遇到麻烦,例如比较一个对象的对象和一个hibernate代理。

在进行了一些调查后,我总结了这个问题:

  • 使用instanceof,你永远不能将重要的成员添加到子类。(没有办法扩展可实例化的类并添加值组件,同时保留equals合约(Bloch)
  • 使用getClass并违反Liskov替换原则

兰格斯说http://www.angelikalanger.com/Articles/JavaSolutions/SecretsOfEquals/Equals.html

  • instanceof测试仅对最终类是正确的,或者至少方法equals()在超类中是最终的。 后者基本上意味着没有子类必须扩展超类的状态,但只能添加与对象的状态和行为无关的function或字段,例如瞬态或静态字段。

另一方面,使用getClass()测试的实现总是符合equals()契约; 他们是正确和强大的。 但是,它们在语义上与使用instanceof测试的实现非常不同。 使用getClass()的实现不允许比较子类和超类对象,即使子类没有添加任何字段,甚至不想重写equals()。 这样一个“普通的”类扩展例如是在为这个“微不足道”的目的而定义的子类中添加调试打印方法。 如果超类通过getClass()检查禁止混合类型比较,那么普通扩展将无法与其超类相比。 这是否是一个问题完全取决于类的语义和扩展的目的。

总结 – 使用finalof with final for equals避免破坏对称性,并避免了hibernate“代理”问题。

链接

  • 利斯科夫和hibernate
  • GetClass vs instanceof in equals

这是一个有趣的问题 – 当涉及inheritance时,将equals实现为等价关系并非易事。 请参阅Martin Odersky等人的这篇深度文章 。 关于实现对象平等。