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

And*_*uta 15 java equality equals

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

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

Gra*_*ray 19

什么是实现等同的最佳实践,在语义上,而不是在技术上.

在Java中,该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);
Run Code Online (Sandbox Code Playgroud)

如果Foo身份是id字段,则第2次fooSet.add(...)应该没有其他元素添加到Set,但应该返回false,因为foo1foo2具有相同id.如果定义Foo.equals(和hashCode)方法包括idstuff领域那么这可能会被打破,因为Set可能含有具有相同id字段2点的参考对象.

如果你没有将对象存储在Collection(或Map)中,那么你不必以equals这种方式定义方法,但是许多人认为它是不好的形式.如果将来你把存储在一个Collection东西中,那么事情就会被打破.

如果我需要测试所有字段的相等性,我倾向于编写另一种方法.喜欢equalsAllFields(Object obj)或类似的东西.

然后你会做类似的事情:

assertTrue(obj1.equalsAllFields(obj2));
Run Code Online (Sandbox Code Playgroud)

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

 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));
Run Code Online (Sandbox Code Playgroud)

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

如何在Java中编写一个等式方法

一些额外的链接:

哦,只是为了子孙后代,无论您选择哪些字段进行比较以确定相等性,您都需要在hashCode计算中使用相同的字段. equals并且hashCode必须是对称的.如果两个对象相等,则它们必须具有相同的哈希码.相反的情况不一定如此.