【问题标题】:What are ways to keep hashCode/equals consistent with the business definition of the class?有哪些方法可以使 hashCode/equals 与类的业务定义保持一致?
【发布时间】:2011-12-03 23:30:02
【问题描述】:

Object javadocs 和 Josh Bloch 告诉我们很多关于应该如何实现 hashCode/equals 的内容,好的 IDE 将正确处理各种类型的字段。一些关于here 的讨论。

这个问题是关于下一步的:你如何确保它们保持良好?

特别是,我觉得对于大多数类,equals/hashCode 应该按照 Bloch 的建议(以及 Eclipse 和其他 IDE 的实现)实现,并考虑到该类上所有非派生的业务逻辑字段。在为类添加新字段作为后续工作的一部分时,人们经常忘记将它们添加到 equals/hashCode 实现中。这可能会导致难以发现的错误,即两个对象看起来相等,但实际上不同之处在于最近引入的字段的值。

当成员字段发生变化时,一个团队(甚至是一个团队!)如何帮助确保类上的 equals/hashCode 继续考虑所有相关字段?

我知道 Apache 的 EqualsBuilder 和 HashCodeBuilder 可以使用反射,这显然会考虑到正确的字段,但我想避免使用它们的性能成本。是否有其他方法来标记不包含在 equals/hashCode 中的字段,并且应该是?静态代码分析、IDE 特性、单元测试技术?

【问题讨论】:

  • 在你有分析器告诉你之前不要担心反射的性能。你可能在不知不觉中到处使用它。
  • @RyanStewart 好点,应该更清楚。对于某些类来说,这不是问题,但对于某些类来说(通过探查器演示),我想要一种方法来确保 equals/hashCode 正在处理不使用反射的字段以及随之而来的运行时开销。

标签: java hashcode


【解决方案1】:

从未尝试过,但是http://code.google.com/p/equalsverifier/ 怎么样?

【讨论】:

  • 看起来很有希望,我会试试看。
  • +1 不错。看起来它甚至可以抑制您知道不相关的字段。它似乎也正是我一直在寻找的东西。
  • 我想使用它,但为了解决我的问题,我想使用“allFieldsShouldBeUsed”标志,不幸的是它还包括静态的最终字段。我相信它很快就会修复 (code.google.com/p/equalsverifier/issues/…),但现在看起来 Lombok 是我的答案。
【解决方案2】:

this question 似乎提供了一个可能的答案。

我对@9​​87654322@ 的研究不多,但我立刻想到,嗯,注释可以与代码生成器一起使用。

【讨论】:

  • +1 这个工具真的很棒。在编译时自动生成诸如 equals() 和 hashCode() 之类的样板代码意味着将来对该类的任何更新都会自动将这些更改合并到这些方法的实现中。
【解决方案3】:

如何为每个要保护的类编写单元测试。 单元测试应该

  1. 使用反射计算字段数,并与本地存储的字段名称(和类型)进行比较。
  2. 如果检测到字段数量(或字段类型)发生变化,则调用哈希码。如果值相同,则测试用例失败。提醒开发人员类的字段已更改,开发人员应:更新 equals 和 Hashcose,并更新 unittest。
  3. 确保单元测试在夜间构建中运行。

【讨论】:

  • 这无济于事。如果添加新字段的开发人员也没有编辑单元测试,则测试将继续传递旧逻辑,即使类字段现在与 equals 和 hashCode 方法不同步。
  • 我认为发帖人的观点是,如果字段数发生了变化,hashcode的值不应该相同。似乎有一个隐含的持久层......或者这个单元测试将有两个硬编码字段的一般概念,一个 int 表示上次更新测试时正在测试的对象中的字段数,一个 long 表示哈希码。但是,是的,充其量只是脆弱的提醒。
  • 单元测试可以使用反射填充两个对象在一个字段上不同的所有字段,然后测试equals
  • @JoopEggen,这似乎是一个更好的方法。您应该提出它作为答案。它脆弱的唯一方式是它依赖于每个字段都是确定相等性的组成部分(并非总是如此,但听起来它适用于 OP)。
  • @JoopEggen 怎么样.. UnitTest 维护一个字段列表。每次执行测试时,它都会将它们与它在运行时使用反射找到的字段进行比较。如果找到的字段集中有任何不匹配,那么它将使测试用例失败。失败的 msg / java 注释,将提醒开发人员他们必须更新更新“Equals+Hashcode”并更新此 UnitTest,每次将新字段添加到类中。
【解决方案4】:

您不需要在哈希码方法中包含每个字段。如果您在哈希码算法中使用了多个字段,那么它很可能会保持良好状态。我见过的 IDE 生成的算法使用随机(在实现时,而不是在执行时)质数常量值,因此只需确保可能最终出现在同一个映射或树中的类(例如,它们实现相同)接口)具有不同的常数值。当然,除非您希望在接口级别实现平等。

我从未见过哈希码算法“变坏”——您对此感到非常担心。

【讨论】:

  • 正如问题所述,我担心 hashCode AND equals。我更喜欢影响 equals 的所有字段也影响 hashCode,因为这样可以保证两者之间的一致性(请参阅我包含的问题的最佳答案)。而对于 equals(),我希望包含所有相关的业务领域。
  • 您是因为想要遵循标准而尝试遵循标准,还是在解决一些问题?你的equals和hash code方法是否经常不同步?如果您的团队如此频繁地修改业务对象的属性,以至于您需要一种自动化的方法来解决这个问题,那么您的团队就会遇到更大的问题。如果您已经掌握了要捕获的对象和进程,那么您对哈希码的第一次遍历应该已经足够好了。
  • 绝对解决一些问题。问题不在于 equals/hashcode 与 EACH OTHER 不同步。这是因为添加了新的业务逻辑字段(例如由于增强),它们应该被添加到 equals() 中。如果您忘记更新 equals(),则 equals()(和 hashCode)与类定义不同步。这就是我试图抓住的情况。
【解决方案5】:

您可以使用使用反射查找属性的工具(例如 XStream)将对象序列化为字符串,并将该字符串存储在存储库中。您的单元测试可以重新序列化您的对象并将结果与​​您存储的值进行比较。

作为您自己流程的一部分,这些字符串的存储取决于手动验证您的 hashCode 和 equals 是否正确捕获了所有相关值。

【讨论】:

  • 这个想法是每当添加字段时测试都会失败,以提醒人们应该更新哈希码/等于?
  • 是 - 添加、删除或重命名。此外,我认为并非对某个领域的每一次更改都必然意味着对平等的影响。您的自动化测试可以检测到您的类发生了变化,但是对 hashcode/equals 的正确评估需要一些人工智慧。
【解决方案6】:

听起来您正在寻找的是 IDE 中的插件或功能,它执行类分析并在 equals() 和 hashCode() 方法未引用所有相关字段时生成警告。

这基本上会将反射开销转移到 IDE,而不是让它在运行时产生成本。

不幸的是,我不知道有什么插件可以做到这一点,但我肯定会看看。

另一方面,这样的自动化工具如何知道哪些字段与业务逻辑相关?您可能需要用注释之类的东西标记不相关的字段,否则您可能会发现自己无法摆脱但您知道不正确的警告。

编辑:这个答案没有用,所以我只是在这里从更好的答案中编译真正有用的建议:

  • Project Lombok - Eclipse 插件,也可与构建工具一起使用以提供自动生成 equals()hashCode() 和其他样板代码 - 因为它是在编译时生成的,所以对类的任何更改也会自动更新这些方法
  • equalsverifier - 提供一种通过反射自动单元测试 equals() 的方法 - 不支持测试 hashCode()

【讨论】:

  • 对于这样的工具,如果它假设一切都是相关的,我会很好。如果我可以注释不应该包含的字段,那就更好了。
猜你喜欢
  • 2010-09-29
  • 1970-01-01
  • 2015-08-22
  • 2013-07-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多