12

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

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

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

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

但据我了解,如果这样做会出现另一个潜在问题:使用 getClass 的原因是为了确保 equals 合约的对称属性。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.*

并且通过使用instanceof,可能不是对称的。示例:B 扩展 A。A 的 equals 对 A 进行 instanceof 检查。B 的 equals 对 B 进行 instanceof 检查。给 A a 和 B b:

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

如何在不丢失对称属性的情况下使用休眠实现等于?使用getClass时我似乎不安全,使用instanceof时我不安全?

答案是永远不要向子类添加重要成员,然后安全地使用 instanceof(对于休眠)?

我阅读的资料来源:

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

Josh Blochs 优秀著作《Effective Java》中的第 7 项和第 8 项,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

4

3 回答 3

4

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

于 2013-10-23T13:12:15.610 回答
3

在评估了更多之后,我将这个问题总结为:

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

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

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

另一方面,使用 getClass() 测试的实现总是遵守 equals() 契约;它们是正确且稳健的。然而,它们在语义上与使用 instanceof 测试的实现非常不同。使用 getClass() 的实现不允许将子类与超类对象进行比较,即使子类不添加任何字段并且甚至不想覆盖 equals() 也是如此。例如,这种“微不足道”的类扩展将是在为这个“微不足道”目的而定义的子类中添加调试打印方法。如果超类通过 getClass() 检查禁止混合类型比较,那么微不足道的扩展将无法与其超类进行比较。

总结 - 使用 instanceof 和 final for equals 避免破坏对称性,并避免休眠“代理”问题。

链接

于 2013-05-26T15:42:28.187 回答
2

这是一个有趣的问题——equals当涉及到继承时,实现为等价关系并非易事。请参阅Martin Odersky 等人的这篇深入文章。关于实现对象相等。

于 2013-02-10T22:34:08.957 回答