库函数使用函数进行bean比较?

时间:2015-04-01 14:46:20

标签: java lambda java-8

由于Java 8比以往更容易直接引用方法(Function / lambda),传统的基于反射的bean比较器(例如common-lang的EqualsBuilder)现在可以干净地实现而不需要反射。我想知道这已经在任何知名图书馆都可以获得吗?

为了清楚起见,我正在使用类似于此签名的功能:

static <T> boolean equals(T a, T b, Function<T, ?>... propRefs);

或者,使用Streams实现这一点的最混乱方式是什么?

1 个答案:

答案 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);
}