Java 为equals()实现选择字段的最佳实践
在编写单元测试时,我经常遇到这样的情况:测试中的某些对象的Java 为equals()实现选择字段的最佳实践,java,equals,equality,Java,Equals,Equality,在编写单元测试时,我经常遇到这样的情况:测试中的某些对象的equals(),在assertEquals中,应该与实际环境中的工作方式不同。以某个界面ReportConfig为例。它有id和几个其他字段。从逻辑上讲,当它们的ids匹配时,一个配置等于另一个配置。但是当涉及到测试一些特定的实现时,比如说,XmlReportConfig,显然我想要匹配所有的字段。一种解决方案是在测试中不使用equals,只需迭代对象属性或字段并比较它们,但这似乎不是一个好的解决方案 因此,除了这种特定类型的情况外,我
equals()
,在assertEquals
中,应该与实际环境中的工作方式不同。以某个界面ReportConfig
为例。它有id
和几个其他字段。从逻辑上讲,当它们的id
s匹配时,一个配置等于另一个配置。但是当涉及到测试一些特定的实现时,比如说,XmlReportConfig
,显然我想要匹配所有的字段。一种解决方案是在测试中不使用equals
,只需迭代对象属性或字段并比较它们,但这似乎不是一个好的解决方案
因此,除了这种特定类型的情况外,我想整理一下在语义上而不是技术上实现equals的最佳实践。从
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李>
至于您的具体情况,如果您在测试中需要更广泛的平等范围,那么您可以在测试中实现它。你不应该仅仅为了使equals方法适合而修改它。你应该在equals中使用所有重要变量,即其值不是从其他变量派生的变量 这来自有效的Java: 对于类中的每个“重要”字段,检查参数的该字段是否与该对象的相应字段匹配
如果您想匹配id,因为它是该类的唯一标识符,那么只需比较id值,在这种情况下不要使用equals 如果您有一个唯一的标识符,该语言不允许您强制执行没有其他具有该标识符的对象,或者其他变量的值匹配。但是,您可以在类的文档中定义它,并且可以在equals实现或其他地方使用断言,因为它是由类的语义给定的不变量 在语义上而不是技术上,实现equals的最佳实践是什么 在Java中,
equals
方法确实应该考虑,因为它是如何与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
字段,则第二个fooSet.add(…)
不应向集合添加另一个元素,而是应返回false
,因为foo1
和foo2
具有相同的id
。如果您将Foo.equals
(和hashCode)方法定义为同时包含id
和stuff
字段,则这可能会被破坏,因为集合
可能包含对具有相同id字段的对象的两个引用
如果不将对象存储在集合
(或映射
)中,则不必以这种方式定义等于
方法,但许多人认为它是一种糟糕的形式。如果将来你把它储存在一个集合中
,那么东西就会坏掉
若我需要测试所有字段的相等性,我倾向于编写另一个方法。类似于equalsAllFields(objectobj)
之类的东西
然后你会做一些类似的事情:
assertTrue(obj1.equalsAllFields(obj2));
此外,适当的做法是不要定义考虑可变字段的等于方法。当我们开始讨论类层次结构时,这个问题也变得很困难。如果子对象将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:根据可变字段定义相等”部分:
其他一些链接:
哦,为了子孙后代,无论您选择比较哪些字段来确定相等性,您都需要在hashCode
计算中使用相同的字段<代码>等于和哈希代码必须是对称的。如果两个对象相等,则它们必须具有相同的哈希代码。相反并不一定正确。我认为重写equals()
方法的唯一最佳实践是常识
除了JavaAPI的定义之外,没有任何规则。一旦选择了相等的定义,就必须将其应用于hashCode()
方法
我的意思是,作为一名开发人员,您、您的同事和维护人员应该知道您的西瓜实例何时等于另一个对象实例。编写equals()
时,我不会考虑单元测试
您可以使用一个或多个组定义每个对象的相等性