自定义等于类定义之外

时间:2016-09-26 21:18:55

标签: java equals comparator

在尝试比较两个对象但不依赖于那些对象的内部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对象。我如何通过处理我感兴趣的每个字段是否相等来断言?

3 个答案:

答案 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 ComparablecompareTo()方法) - 在我们的例子中,Foo 。如果Foo有一个Bar getBar()访问者必须包含在比较中,那么请转到基元 - .thenComparing(f -> f.getBar().getName())或使Bar实现Comparable。如果你走自定义lambda函数的路线,那就不要忘记正确处理空值 - 这有时是对它自己的挑战。

此答案中的方法是积极的,比较器定义了存储在列表中的对象集的总顺序。这种方法的不利之处在于,这种总顺序并不是真正需要简单比较 - 如果它真的是你需要的全部。在某些情况下,写一个好的旧循环并进行所有比较&#34;手动&#34;可能不那么令人困惑。根据经验,在大多数情况下,订单是有益的,如果不是现在,那么在下一个版本中。