问题描述
我有一个数据类。字段可以是集合、原语、引用等。我必须检查这个类的两个实例的相等性。通常我们会为此目的重写 equals 方法。但是用例是这样的,要比较的属性可能会有所不同。
所以说,A 类有属性:
int name:
int age:
List<String> hobbies;
在一次调用中,我可能必须根据姓名、年龄检查相等性,而对于另一次调用,我可能必须检查姓名、爱好的相等性。
实现这一目标的最佳做法是什么?
解决方法
假设您想总是通过 2 个属性进行比较,那么在 Java 8 中最好使用一等函数。
private boolean equalNameAndAge(A a1,A a2) {
return compareByTwoParameters(a1,a2,A::getName,A::getAge);
}
private boolean equalNameAndHobbies(A a1,A::getHobbies);
}
private boolean compareByTwoParameters(A a1,A a2,Function<A,?>... functions) {
if (a1 == null || a2 == null) {
return a1 == a2;
}
for (Function<A,?> function : functions) {
if (!Objects.equals(function.apply(a1),function.apply(a2))) {
return false;
}
}
return true;
}
,
anyMatch
您可以使用 Stream API 解决这个问题,使用 anyMatch
,您的规则基本上被定义为 Predicate
。
例如检查是否有人是 20
岁:
List<Person> persons = ...
boolean doesAnyMatch = persons.stream()
.anyMatch(p -> p.getAge() == 20);
您当然也可以通过与现有项目进行比较的方式来设置规则,更多地模仿 equals
:
p -> p.getAge() == otherPerson.getAge()
Predicate
您可以在其他地方设置所有规则,作为 Predicate
并使用它们。例如:
List<Predicate<Person>> rules = List.of(
p -> p.getAge() == 20,p -> p.getName().equals("John"),p -> p.getAge() > 18,p -> p.getName().length() > 10 && p.getAge() < 50
);
然后也许可以在某种循环中使用它们,无论您需要什么:
for (Predicate rule : rules) {
boolean doesAnyMatch = persons.stream()
.anyMatch(rule);
...
}
findAny
您可以将 anyMatch
替换为 filter
和 findAny
的组合以接收实际匹配,即 Person
,而不仅仅是 boolean
:
Person matchingPerson = persons.stream()
.filter(rule)
.findAny();