在尝试比较两个对象但不依赖于那些对象的内部equals()方法时,有没有办法紧凑地编写自定义equals方法?例如,如果我有两个这样的Foo对象:
public class Foo {
int id;
String name;
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Foo item = (Foo) o;
return id == item.id && listId == item.name;
}
}
但是,在foo对象的用例中,让我说我只想让它们等同于id。请记住,这是一个玩具示例,真实用例有更多字段,因此我可能有一个对象,其中有6个字段全部用于重写的equals方法,但可能只想使用其中3个在类外执行等于比较两个对象时。
List<Foo> objType1;
List<Foo> objType2;
比较两个列表并断言每个Foo对象相等但仅在比较中使用Foo字段的子集。我不想以任何方式触摸实际的Foo对象。我如何通过处理我感兴趣的每个字段是否相等来断言?
答案 0 :(得分:0)
您可以在Foo类中添加一些额外的字段,这些字段将在您的overriden equals方法中使用,以指定应该使用哪些字段来确定两个实例是否相等。然后,您可以在比较之前设置这些字段。那么你的equals方法可能包含:
if ( useFiledA ) {
if ( this.a != item.a ) return false;
}
if ( useFiledB ) {
if ( this.b != item.b ) return false;
}
// etc.
return true;
答案 1 :(得分:0)
从您的类实现Comparable。然后编写compareTo方法。迭代第一个列表并在第二个列表的所有元素上调用colpareTo
答案 2 :(得分:0)
解决方案的复杂性实际上取决于用例。普遍的 - 不,没有直接的字节码操作就无法做到这一点。 许多Java API允许使用自定义Comparator作为选项。例如。如果你想用自定义比较器比较两个Foo列表:
List<Foo> list1 = ...
List<Foo> list2 = ...
Comparator<Foo> c = Comparator.comparing(Foo::getId);
boolean equal = list1.size() == list2.size() &&
IntStream.range(0, list1.size())
.allMatch(i -> c.compare(list1.get(i), list2.get(i)) == 0);
注意,此解决方案不会检查list1或list2是否为null,并假设您的Foo具有getId()等标准getter。此外,如果您不处理列表但使用抽象的可迭代集合,您可能需要查看zip实现。 Comparator.comapring()
可以像Comparator.comparing(Foo::getId).thenComparing(Foo::getAttrX).thenComparing(Foo:getAttrY)...
一样链接;这是相当方便和可读的。
您可能希望查看针对各种情况自定义equals()
的另一个选项是将Proxy.newProxyInstance()
与您的自定义equals
覆盖一起使用,即在您的实例周围自动创建代理包装器在填写馆藏等时。
更新
使用Comparator.comparing().thenComparing()...
链似乎很棘手。有助于理解这些函数的lambdas需要从给定的顶级对象引用中提取原语或可比较的后代(即implements Comparable
和compareTo()
方法) - 在我们的例子中,Foo
。如果Foo
有一个Bar getBar()
访问者必须包含在比较中,那么请转到基元 - .thenComparing(f -> f.getBar().getName())
或使Bar
实现Comparable
。如果你走自定义lambda函数的路线,那就不要忘记正确处理空值 - 这有时是对它自己的挑战。
此答案中的方法是积极的,比较器定义了存储在列表中的对象集的总顺序。这种方法的不利之处在于,这种总顺序并不是真正需要简单比较 - 如果它真的是你需要的全部。在某些情况下,写一个好的旧循环并进行所有比较&#34;手动&#34;可能不那么令人困惑。根据经验,在大多数情况下,订单是有益的,如果不是现在,那么在下一个版本中。