由于Java 8比以往更容易直接引用方法(Function / lambda),传统的基于反射的bean比较器(例如common-lang的EqualsBuilder
)现在可以干净地实现而不需要反射。我想知道这已经在任何知名图书馆都可以获得吗?
为了清楚起见,我正在使用类似于此签名的功能:
static <T> boolean equals(T a, T b, Function<T, ?>... propRefs);
或者,使用Streams实现这一点的最混乱方式是什么?
答案 0 :(得分:0)
给出一个类似
的示例类class Person {
long id;
String name, surname;
int age;
public long getId() {
return id;
}
public String getName() {
return name;
}
public String getSurname() {
return surname;
}
public int getAge() {
return age;
}
}
当然,我们可以用最紧凑的形式来做,比如
@Override
public boolean equals(Object obj) {
if(obj==this) return true;
if(!(obj instanceof Person)) return false;
Person p=(Person)obj;
return Stream.<Function<Person,?>>of(
Person::getName, Person::getSurname, Person::getAge)
.allMatch(f->Objects.equals(f.apply(this), f.apply(p)));
}
但与简单形式
相比,它提出了一个重要的问题:它是否真的是一场胜利@Override
public boolean equals(Object obj) {
if(obj==this) return true;
if(!(obj instanceof Person)) return false;
Person p=(Person)obj;
return this.age==p.age && Objects.equals(this.name, p.name)
&& Objects.equals(this.surname, p.surname);
}
因为它不会增加代码的简洁性,也不会阻止,忘记重要的属性,也不会阻止此equals
实现与hashCode
实现之间的不匹配。
请注意,这也适用于EqualsBuilder
;它并没有真正帮助解决最关键的问题(实际上,我看不到任何优势)。
如果我们想从中获得优势,我们不得不求助于一个不那么紧凑的实现:
static List<Function<Person,?>> EQ_PROPS=Arrays.asList(
Person::getName, Person::getSurname, Person::getAge);
@Override
public boolean equals(Object obj) {
if(obj==this) return true;
if(!(obj instanceof Person)) return false;
Person p=(Person)obj;
return EQ_PROPS.stream().allMatch(f->Objects.equals(f.apply(this), f.apply(p)));
}
@Override
public int hashCode() {
return Objects.hash(EQ_PROPS.stream().map(f->f.apply(this)).toArray());
}
我们仍然不能保证没有相关属性丢失,但至少我们需要检查一个责任点,我们确保相等和哈希码是一致的,所以最后< em>一个优于手动实现的优势。
如果您担心临时对象的性能影响,您可以将所有功能组合到实际操作中,无需任何临时对象即可执行:
static List<Function<Person,?>> EQ_PROPS=Arrays.asList(
Person::getName, Person::getSurname, Person::getAge);
static BiPredicate<Person,Person> EQUAL=EQ_PROPS.stream()
.<BiPredicate<Person,Person>>map(f -> (a,b) -> Objects.equals(f.apply(a), f.apply(b)))
.reduce(BiPredicate::and).get();
static ToIntFunction<Person> HASH=EQ_PROPS.stream()
.<ToIntFunction<Person>>map(f -> a -> Objects.hash(f.apply(a)))
.reduce((f,g) -> x -> f.applyAsInt(x)*31+g.applyAsInt(x)).get();
@Override
public boolean equals(Object obj) {
return obj==this || (obj instanceof Person)&& EQUAL.test(this, (Person)obj);
}
@Override
public int hashCode() {
return HASH.applyAsInt(this);
}