为equals()实现选择字段的最佳实践

时间:2012-03-16 14:31:25

标签: java equals equality

在编写单元测试时,我常常遇到equals()测试中某些对象的情况 - assertEquals - 应该与它在实际环境中的工作方式不同。以一些接口ReportConfig为例。它有id和其他几个字段。逻辑上,当id匹配时,一个配置等于另一个配置。但是,当涉及到测试某些特定实现时,比如XmlReportConfig,显然我希望匹配所有字段。一种解决方案是不在测试中使用equals,只是迭代对象属性或字段并进行比较,但它似乎不是一个好的解决方案。

因此,除了这种特定类型的情况之外,我想要理清什么是在语义上而不是在技术上实现平等的最佳实践。

5 个答案:

答案 0 :(得分:18)

  

从技术角度而言,从技术上讲,实现equals的最佳实践是什么。

在Java中,equals方法确实应该被视为"identity equals",因为它与CollectionMap实现的集成方式。请考虑以下事项:

 public class Foo() {
    int id;
    String stuff;
 }

 Foo foo1 = new Foo(10, "stuff"); 
 fooSet.add(foo1);
 ...
 Foo foo2 = new Foo(10, "other stuff"); 
 fooSet.add(foo2);

如果Foo身份是id字段,那么第二个fooSet.add(...)应该将另一个元素添加到Set但是应该返回{{ 1}}因为falsefoo1具有相同的foo2。如果您定义id(和hashCode)方法以包含两者 Foo.equalsid字段,那么这可能会被打破,因为stuff可能包含对具有相同id字段的对象的2个引用。

如果您没有将对象存储在Set(或Collection)中,那么您不必以这种方式定义Map方法,但许多人认为是不好的形式。如果您将来 将其存储在equals中,那么事情将会被破坏。

如果我需要来测试所有字段的相等性,我倾向于编写另一种方法。像Collection或类似的东西。

然后你会做类似的事情:

equalsAllFields(Object obj)

此外,正确的做法是定义考虑可变字段的assertTrue(obj1.equalsAllFields(obj2)); 方法。当我们开始讨论类层次结构时,问题也变得困难。如果子对象将equals定义为其本地字段基类equals的组合,则会违反其对称性:

equals

我强烈推荐的更多内容是这个伟大页面中的“陷阱#3:在可变字段方面定义平等”部分:

  

How to Write an Equality Method in Java

其他一些链接:

哦,只是为了子孙后代,无论您选择比较什么字段来确定相等性,您都需要在 Point p = new Point(1, 2); // ColoredPoint extends Point ColoredPoint c = new ColoredPoint(1, 2, Color.RED); // this is true because both points are at the location 1, 2 assertTrue(p.equals(c)); // however, this would return false because the Point p does not have a color assertFalse(c.equals(p)); 计算中使用相同的字段。 hashCodeequals必须是对称的。如果两个对象相等,则必须具有相同的哈希码。相反的情况不一定如此。

答案 1 :(得分:4)

Object.equals(Object obj) javadoc:

复制

指示某个其他对象是否“等于”此对象。

equals方法在非null对象引用上实现等价关系:

  • 它是自反的:对于任何非空引用值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。

我很清楚,这就是平等应该如何运作。至于选择哪个字段,您可以选择需要哪个字段组合来确定某个其他对象是否“等于”此字段

至于你的具体情况,如果你在测试中需要更广泛的平等范围,那么你在测试中实现它。你不应该破解你的等于方法,只是为了使它适合。

答案 2 :(得分:1)

您应该使用所有重要变量,即其值不是来自其他变量的变量,等于。

这来自Effective Java:

  

对于类中的每个“重要”字段,检查参数的该字段是否与该对象的相应字段匹配。

如果你想匹配id,因为它是该类的唯一标识符,那么只需比较id值,在这种情况下不要使用equals。

如果您有唯一标识符,则该语言不允许您强制执行没有其他具有该标识符的对象或其余变量值匹配。但是,您可以在类的文档中定义它,并且可以在equals实现或其他地方使用断言,因为它是由类的语义给出的不变量。

答案 3 :(得分:1)

在编写equals()两者时,我不会考虑单元测试

通过实施equals()hashcode(),您可以使用一个或一组属性定义每个对象的相等性

在测试中,如果要比较对象的所有属性,那么显然您需要调用每个方法。

我认为最好单独对待它们。

答案 4 :(得分:0)

我认为覆盖equals()方法时唯一的最佳做法是常识。

除了Java API的equivalence definition之外,没有规则。一旦选择了相等的定义,您也必须将它应用于hashCode()方法。

我的意思是,作为开发人员,您,您的同事和维护者应该知道您的实例何时说Watermelon等于另一个Object实例。