由于 Java 8 比以往任何时候都更容易直接引用方法(Function/lambda),因此传统的基于反射的 bean 比较器(例如 common-lang 的 EqualsBuilder
)现在可以干净地实现,无需反射。我想知道这是否已在任何知名图书馆中可用?
需要明确的是,我正在寻找与此签名类似的功能:
static <T> boolean equals(T a, T b, Function<T, ?>... propRefs);
或者,使用 Streams 实现这一点的最不困惑的方式?
最佳答案
给出一个示例类,例如
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());
}
我们仍然不能保证没有丢失相关属性,但至少我们有一个需要检查的单点责任,并且我们确保了相等性和哈希码一致,所以最终比手动实现有一个优势。
如果您担心临时对象对性能的影响,您可以将所有功能组合到实际操作中,无需任何临时对象即可执行:
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);
}
关于java - 使用函数进行bean比较的库函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29393887/