【问题标题】:Override equals and hashCode just to call super.equals/hashCode or throw AssertionError?重写 equals 和 hashCode 只是为了调用 super.equals/hashCode 或抛出 AssertionError?
【发布时间】:2023-03-29 23:04:02
【问题描述】:

是否有最佳实践,何时覆盖 equals?

我是否应该覆盖 equals/hashCode 并抛出 AssertionError?只是为了确保没有人使用它? (正如《Effective Java》一书中推荐的)

我是否应该重写 equals/hashCode 并只调用 super.equals/hashCode,因为超类的行为是相同的? (FindBugs 推荐这个,因为我加了一个字段)

它们真的是最佳实践吗?

【问题讨论】:

  • Effective Java 建议在非常有限的情况下抛出 AssertionError:“该类是私有的或包私有的,并且您确定它的 equals 方法永远不会被调用。”只为了调用超类方法而重写方法与不重写方法是一样的。

标签: java


【解决方案1】:

错误

您不应从equalshashCode 方法中抛出异常。 equals 和 hashCode 方法无处不在,在这里不加选择地抛出异常可能会在以后伤害您。

断言错误

你不应该直接抛出AssertionError。将assert 语句放入任何方法都可以,因为关闭断言时这些语句将不会运行。

何时覆盖

如果超类方法是Object.equals,则覆盖并直接传递给super.equals() 没有害处。

如果您要比较的两个对象属于不同类型,那么您可能会陷入破坏对称性的陷阱,即x.equals(y) 为真而y.equals(x) 为假。如果 y 是 x 的子类,则可能会发生这种情况,因此 y 的 equals 方法可能会进行稍微不同的比较。您可以使用 if (getClass() != obj.getClass()) return false; 在您的 equals 方法中检查这一点。

最佳实践

Effective Java 是一个很好的资源,特别是关于如何最好地实现hashCode() 方法。确保您阅读并考虑了 Javadoc 中列出的 equalshashCode 的合同,并确保您覆盖其中一个,然后覆盖另一个。 eclipse 中的“Generate hashCode() and equals”函数很好地提供了这些方法中所需的内容,因此请查看它为您的类生成的代码。以下摘自Javadoc of java.lang.Object

equals Contract:

  • 它是自反的:对于任何非空引用值 x,x.equals(x) 应该返回 true。
  • 它是对称的:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应该返回 true。
  • 它是可传递的:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true 并且 y.equals(z) 返回 true,则 x.equals(z) 应该返回 true .
  • 这是一致的:对于任何非空引用值 x 和 y,x.equals(y) 的多次调用始终返回 true 或始终返回 false,前提是没有修改对象上 equals 比较中使用的信息。李>
  • 对于任何非空引用值 x,x.equals(null) 应该返回 false。

hashCode Contract:

  • 只要在 Java 应用程序执行期间对同一个对象多次调用,hashCode 方法必须始终返回相同的整数,前提是没有修改对象上的 equals 比较中使用的信息。该整数不需要在应用程序的一次执行与同一应用程序的另一次执行之间保持一致。
  • 如果两个对象根据 equals(Object) 方法相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。
  • 如果根据 equals(java.lang.Object) 方法,如果两个对象不相等,则不要求对两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

【讨论】:

  • 我不同意以下几点:(1)如果参数为 null,equals 的结果应该是“false”,而不是 NPE,(2)很容易检查每个可为空的属性对象做正确的事,只有当目标属性也为空时才等于。无需抛出 NPE。
  • 关于抛出 NullPointerException 和 ClassCastException 的部分是不正确的。 equals(null) 应该返回 false。传入不同的类型是完全有效的,比如使用 HashSet 来保存混合类型。
  • @Steve Kuo 感谢您的接机。我让我的电线与compareTo() 方法交叉,您可以期望这些异常被抛出。答案已更改以反映您的 cmets。
【解决方案2】:

如果你需要比较同一个类中的两个对象,你应该用你自己的实现覆盖equals。如果要覆盖 equals,则还应提供 hashCode 的实现。重写方法只是为了调用 super 是没有意义的,因为这是默认行为。我不会重写抛出 AssertionError 的方法,因为这意味着您永远无法比较对象的相等性或在 HashMap 中使用它们。在这种情况下,Collections 类还有其他含义。

【讨论】:

  • 我刚刚看到 krock 写的内容,这让我想起了 equals 和 hashCode 的 API。您应该遵循这些方法的 API 规范,而不是抛出意外异常。
【解决方案3】:

我为将在 java.util Collections API 中使用的所有对象覆盖 equals 和 hashCode。这些类通常依赖于适当的覆盖。

【讨论】:

    【解决方案4】:

    覆盖equalshashCode,然后用super.equals() 调用超类实现对我来说只是一种非常清晰的记录方式,您已经考虑过并且认为超类实现仍然完全有效。就个人而言,我认为仅在 JavaDoc 类中记录这一点并跳过添加 equals 和 hashCode 没有任何问题。

    抛出 AssertionError 有点极端,并没有完全被最小意外原则所涵盖。如果在您的对象上调用equals()hashCode() 确实是错误的,请改用UnSupportedOperationException

    如果您要扩展的对象确实支持相等,那么最好自己支持它,方法是调用 super.equals(),然后比较您添加的其他成员。

    【讨论】:

    • 为什么不在类的 JavaDocs 中记录相等性?
    • 这正是我所做的......我将更新答案以使其更清晰
    猜你喜欢
    • 1970-01-01
    • 2021-03-11
    • 2013-06-02
    • 2013-07-28
    • 1970-01-01
    • 1970-01-01
    • 2015-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多