为equals()实现选择字段的最佳实践 [英] Best practice to choose fields for equals() implementation
问题描述
在编写单元测试时,我经常遇到以下情况:测试中某些对象的 equals()
-在 assertEquals $ c中$ c>-应该与实际环境中的工作方式不同。以某些接口
ReportConfig
为例。它具有 id
和其他几个字段。逻辑上,当它们的 id
匹配时,一个配置等于另一个。但是在测试某些特定的实现时,例如 XmlReportConfig
,显然我想匹配所有字段。一种解决方案是不要在测试中使用 equals
并仅遍历对象属性或字段并进行比较,但这似乎不是一个好的解决方案。
When writing unit-tests, I often face the situation when equals()
for some object in tests -- in assertEquals
-- should work differently from how it works in actual environment. Take for example some interface ReportConfig
. It has id
and several other fields. Logically, one config equals to another one when their id
s match. But when it comes to testing some specific implementation, say, XmlReportConfig
, obviously I want to match all fields. One solution is not to use equals
in tests and just iterate over the object properties or fields and compare them, but it doesn't seem like a good solution.
因此,除了这种特殊情况外,我想从语义上而不是从技术上梳理实现平等的最佳实践。
So, apart from this specific type of situations, I want to sort out what are best practices to implement equals, semantically, not technically.
推荐答案
在语义上而非技术上实现平等的最佳实践是什么。
what are best practices to implement equals, semantically, not technically.
在Java中,等于
方法实际上应被视为身份等于 是因为它如何与 Collection
和 Map
实现。请考虑以下内容:
In Java the equals
method really should be considered to be "identity equals" because of how it integrates with Collection
and Map
implementations. Consider the following:
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
,但应返回 false
,因为 foo1
和 foo2
具有相同的 id
。如果定义 Foo.equals
(和hashCode)方法以同时包含 , id
和 stuff
字段,则此字段可能会被破坏,因为 Set
可能包含2个对具有相同id字段的对象的引用。
If Foo
identity is the id
field then the 2nd fooSet.add(...)
should not add another element to the Set
but should return false
since foo1
and foo2
have the same id
. If you define Foo.equals
(and hashCode) method to include both the id
and the stuff
fields then this might be broken since the Set
may contain 2 references to the object with the same id field.
如果您没有将对象存储在 Collection
(或 Map
),则不必以这种方式定义 equals
方法,但是许多人认为它是错误的形式。如果将来您要做将其存储在 Collection
中,那么事情将会崩溃。
If you are not storing your objects in a Collection
(or Map
) then you don't have to define the equals
method this way, however it is considered by many to be bad form. If in the future you do store it in a Collection
then things will be broken.
如果我需要测试所有字段的相等性,我倾向于编写另一种方法。像 equalsAllFields(Object obj)
之类的东西。
If I need to test for equality of all fields, I tend to write another method. Something like equalsAllFields(Object obj)
or some such.
然后,您将执行以下操作:
Then you would do something like:
assertTrue(obj1.equalsAllFields(obj2));
此外,正确的做法是不定义等于
方法,该方法考虑了可变字段。当我们开始谈论类层次结构时,这个问题也变得很困难。如果子对象将 equals
定义为其本地字段和的基类 equals
然后违反了它的对称性:
In addition, a proper practice is to not define equals
methods which take into account mutable fields. The problem also gets difficult when we start talking about class hierarchies. If a child object defines equals
as a combination of its local fields and the base class equals
then its symmetry has been violated:
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:根据可变字段定义等式中的部分。
Some more reading I would highly recommend is the "Pitfall #3: Defining equals in terms of mutable fields" section in this great page:
一些其他链接:
- Implementing hashCode() and equals()
- Graceful Blog - Values, Equals, and Hashcodes
哦,只是为了后代,无论您选择比较哪些字段来确定相等性,都需要在 hashCode
计算中使用相同的字段。 等于
和 hashCode
必须对称。如果两个对象相等,则它们必须具有相同的哈希码。相反不一定是正确的。
Oh, and just for posterity, regardless of what fields you choose to compare to determine equality, you need to use the same fields in the hashCode
calculation. equals
and hashCode
must be symmetric. If two objects are equals, they must have the same hash-code. The opposite is not necessarily true.
这篇关于为equals()实现选择字段的最佳实践的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!