9

这个问题与为具有大量字段的对象覆盖 equals() 方法特别相关。首先,让我说这个大对象不能在不违反 OO 原则的情况下分解成多个组件,所以告诉我“没有一个类应该有超过 x 个字段”是没有帮助的。

继续前进,当我忘记检查其中一个字段是否相等时,问题就解决了。因此,我的 equals 方法不正确。然后我想使用反射:

--code removed because it was too distracting--

这篇文章的目的不一定是重构代码(这甚至不是我正在使用的代码),而是获取关于这是否是一个好主意的输入。

优点:

  • 如果添加了新字段,则会自动包含该字段
  • 该方法比 30 个 if 语句简洁得多

缺点:

  • 如果添加了一个新字段,它会自动包含在内,有时这是不可取的
  • 性能:这必须更慢,我觉得没有必要打破分析器
  • 在比较中将某些要忽略的字段列入白名单有点难看

有什么想法吗?

4

9 回答 9

13

如果您出于性能原因确实想要列入白名单,请考虑使用注释来指示要比较的字段。此外,如果您的字段没有很好的equals().

PS如果你走这条路equals(),别忘了做类似的事情hashCode()

PPS 我相信您已经考虑过 HashCodeBuilderEqualsBuilder

于 2008-09-23T23:49:02.497 回答
9

使用 Eclipse,FFS!

删除您拥有的 hashCode 和 equals 方法。

右键单击该文件。

选择 Source->Generate hashcode and equals...

完毕!不再担心反射。

对添加的每个字段重复此操作,您只需使用大纲视图删除您的两个方法,然后让 Eclipse 自动生成它们。

于 2008-10-06T12:43:50.437 回答
5

如果您确实采用反射方法,那么 EqualsBuilder 仍然是您的朋友:

 public boolean equals(Object obj) {
    return EqualsBuilder.reflectionEquals(this, obj);
 }
于 2008-09-24T11:43:10.237 回答
2

如果您担心,这里有一个想法:

1/ 在添加/删除字段时忘记更新用于检查相等性的大量 if 语句。

2/ 在 equals() 方法中执行此操作的性能。

尝试以下操作:

a/ 在您的 equals() 方法中恢复使用长长的 if 语句序列。

b/ 有一个函数,它包含一个字段列表(在一个字符串数组中),它会根据实际情况检查该列表(即反映的字段)。如果它们不匹配,它将引发异常。

c/ 在此对象的构造函数中,对该函数进行同步的一次性调用(类似于单例模式)。换句话说,如果这是该类构造的第一个对象,则调用上面(b)中描述的检查函数。

如果您没有更新 if 语句以匹配反映的字段,那么当您运行程序时,异常会立即显现出来;然后您修复 if 语句并更新上面 (b) 中的字段列表。

对象的后续构造不会执行此检查,您的 equals() 方法将以最大可能的速度运行。

尽我所能,我无法找到这种方法的任何真正问题(StackOverflow 上可能存在更伟大的思想) - 对于运行一次行为的每个对象构造都有一个额外的条件检查,但这似乎相当小。

如果你足够努力,你仍然可以让你的 if 语句与你的字段列表和反映的字段不同步,但例外情况将确保你的字段列表与反映的字段匹配,你只需确保更新 if 语句和字段同时列出。

于 2008-09-24T02:08:27.890 回答
1

您始终可以在 equals 方法中注释您想要/不想要的字段,这应该是一个简单明了的更改。

性能显然与实际比较对象的频率有关,但是很多框架都使用哈希映射,因此您的 equals 可能比您想象的更多地使用。

此外,说到哈希映射,您对 hashCode 方法也有同样的问题。

最后,您真的需要比较所有字段是否相等吗?

于 2008-09-23T23:51:48.653 回答
1

您的代码中有一些错误。

  1. 你不能假设thisobj是同一类。事实上,它被明确允许为obj任何其他类。您可以开始,if ( ! obj instanceof myClass ) return false;但这仍然不正确,因为obj可能是具有this可能重要的附加字段的子类。
  2. 你必须用一个简单的支持nullobjif ( obj == null ) return false;
  3. 您不能将null和空字符串视为相等。null而是特殊对待。这里最简单的方法是从比较开始Field.get(obj) == Field.get(this)。如果它们都相等或恰好都指向同一个对象,则速度很快。(注意:这也是一种优化,因为这是一个缓慢的例程,所以您需要它。)如果失败,您可以使用 fastif ( Field.get(obj) == null || Field.get(this) == null ) return false;来处理恰好是null. 最后你可以使用通常的equals().
  4. 你没有使用foundMismatch

我同意汉克的观点[HashCodeBuilder][1],这[EqualsBuilder][2]是一个更好的方法。它易于维护,不需要大量样板代码,并且您可以避免所有这些问题。

于 2008-09-24T00:02:48.533 回答
1

您可以使用注释从检查中排除字段

例如

@IgnoreEquals
String fieldThatShouldNotBeCompared;

然后当然您检查泛型 equals 方法中是否存在注释。

于 2008-10-06T11:44:25.423 回答
0

如果您可以访问字段的名称,为什么不将您不想包含的字段始终以“local”或“nochk”或类似名称开头作为标准。

然后你将所有以这个开头的字段列入黑名单(代码不是那么难看)。

我不怀疑它有点慢。您需要决定是否要根据执行速度交换易于更新。

于 2008-09-23T23:52:26.857 回答
-1

看看 org.apache.commons.EqualsBuilder:

http://commons.apache.org/proper/commons-lang/javadocs/api-3.2/org/apache/commons/lang3/builder/EqualsBuilder.html

于 2008-09-24T00:05:53.253 回答