在编写单元测试时,我经常遇到这样的情况equals(),即测试中的某些对象的assertEquals工作方式与实际环境中的工作方式不同。以一些接口为例ReportConfig。它具有id和其他几个领域。逻辑上,一个配置等于ids匹配时的另一个配置。但是,当谈到测试某些特定的实现时,例如XmlReportConfig,显然我想匹配 所有 字段。一种解决方案是不要equals在测试中使用,而只是遍历对象的属性或字段并进行比较,但这似乎不是一个好的解决方案。
equals()
assertEquals
ReportConfig
id
XmlReportConfig
equals
因此,除了这种特定类型的情况外,我想从语义上而非技术上梳理实现平等的最佳实践。
在语义上而非技术上,实现最佳实践的最佳方法是平等。
在Java中,由于该equals方法如何与实现集成在一起,因此实际上应将其视为“身份相等”。考虑以下:Collection``Map
Collection``Map
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字段,则第2次fooSet.add(...)应该 没有 其他元素添加到Set,但应该返回false,因为foo1与foo2具有相同id。如果定义Foo.equals(和hashCode)方法包括 两 对id和stuff领域那么这可能会被打破,因为Set可能含有具有相同id字段2点的参考对象。
Foo
fooSet.add(...)
Set
false
foo1
foo2
Foo.equals
stuff
如果您没有将对象存储在Collection(或Map)中,则不必以equals这种方式定义方法,但是许多人认为它是错误的形式。如果将来您 确实 将其存储在a中,Collection那么事情将会崩溃。
Collection
Map
如果我 需要 测试所有字段的相等性,则倾向于编写另一种方法。诸如此类的东西equalsAllFields(Object obj)。
equalsAllFields(Object obj)
然后,您将执行以下操作:
assertTrue(obj1.equalsAllFields(obj2));
另外,一种适当的做法是 不要 定义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));
我强烈建议您阅读以下内容中的“陷阱#3:根据可变字段定义相等项”部分:
如何用Java编写平等方法
一些其他链接:
哦,仅出于后代考虑,无论您选择比较哪些字段来确定相等性,都需要在hashCode计算中使用相同的字段。 equals并且hashCode必须是对称的。如果两个对象相等,则它们 必须 具有相同的哈希码。相反不一定是正确的。
hashCode