Java equals():反映还是不反映

时间:2020-03-06 14:38:05  来源:igfitidea点击:

这个问题专门与覆盖具有大量字段的对象的equals()方法有关。首先,让我说,在不违反OO原则的情况下,不能将这个大对象分解为多个组件,因此告诉我"任何类都不应超过x个字段"将无济于事。

继续前进,当我忘记检查一个领域是否平等时,问题就变成了现实。因此,我的equals方法不正确。然后我想使用反射:

--code removed because it was too distracting--

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

优点:

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

缺点:

  • 如果添加了新字段,则会自动将其包括在内,有时这是不可取的
  • 性能:速度一定要慢一些,我不认为需要分解探查器
  • 将某些字段列入白名单,以便比较时忽略

有什么想法吗?

解决方案

如果出于性能原因确实希望将其列入白名单,请考虑使用注释来指示要比较的字段。另外,如果字段没有对equals()的良好实现,则此实现将无法正常工作。

P.S.如果我们将此路线用于" equals()",请不要忘记对" hashCode()"执行类似的操作。

P.P.S.我相信我们已经考虑过HashCodeBuilder和EqualsBuilder。

我们始终可以在equals方法中注释我们想要/不想要的字段,这应该是对它的直接简单的更改。

性能显然与实际比较对象的频率有关,但是许多框架使用哈希映射,因此使用等号的次数可能超出想象。

同样,谈到哈希映射,hashCode方法也存在相同的问题。

最后,我们是否真的需要比较所有字段的相等性?

如果我们可以访问字段名称,那么为什么不将不希望包含的字段作为标准始终以" local"或者" nochk"或者类似名称开头。

然后,我们将以此开头的所有字段都列入黑名单(然后,代码就不会那么难看了)。

我毫不怀疑它会慢一点。我们需要确定是否要与执行速度交换更新的简便性。

代码中有一些错误。

  • 我们不能假设" this"和" obj"是同一类。实际上,明确允许obj为任何其他类。我们可以以if(!obj instanceof myClass)返回false;开头,但是这仍然是不正确的,因为obj可能是this`的子类,可能还有其他重要的字段。
  • 我们必须使用简单的if(obj == null)return false;来支持obj的null值。
  • 我们不能将null和空字符串视为相等。相反,应特别对待null。这里最简单的方法是通过比较Field.get(obj)== Field.get(this)开始。如果它们相等或者都碰巧指向同一个对象,则速度很快。 (注意:这也是一种优化,因为这是一个缓慢的例程,因此我们需要这样做。)如果失败,则可以使用快速if(Field.get(obj)== null || Field.get(this)= = null)return false;处理正好为" null"的情况。最后,我们可以使用通常的" equals()"。
  • 我们没有使用" foundMismatch"

我同意Hank的观点,[[HashCodeBuilder] [1][EqualsBuilder] [2]是更好的选择。它易于维护,不需要很多样板代码,并且可以避免所有这些问题。

看一下org.apache.commons.EqualsBuilder:

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

如果我们担心,这是一个想法:

1 /忘记更新大量if语句,以便在添加/删除字段时检查是否相等。

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

请尝试以下方法:

a /返回到在equals()方法中使用较长的if语句序列。

b /具有一个包含字段列表(在String数组中)的单个函数,该函数将对照实际情况检查该列表(即反射字段)。如果它们不匹配,它将抛出异常。

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

如果尚未更新if语句以匹配反射字段,则该异常将在运行程序时立即将其显而易见。然后我们修复if语句并从上面的(b)中更新字段列表。

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

尽我所能尝试,但我无法用这种方法发现任何实际问题(StackOverflow上可能存在更大的想法)在每个对象构造上都有一次额外的条件检查,以确保一次运行行为,但这似乎相当小。

如果我们尽力而为,仍然可以使if语句与字段列表和反射字段不一致,但是该例外将确保字段列表与反射字段匹配,并且我们只需确保更新了if语句和字段同时列出。

如果我们采用反射方法,那么EqualsBuilder仍然是朋友:

public boolean equals(Object obj) {
    return EqualsBuilder.reflectionEquals(this, obj);
 }

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

例如

@IgnoreEquals
String fieldThatShouldNotBeCompared;

然后,当然可以在泛型equals方法中检查注释的存在。

使用Eclipse,FFS!

删除我们拥有的hashCode和equals方法。

右键单击该文件。

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

完毕!不再担心反射。

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