java - 使用函数进行bean比较的库函数?

标签 java lambda java-8

由于 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/

相关文章:

java - 登录后触发欢迎屏幕 2-3 秒

java - setContentView() 与 GLSurfaceView 一起使用时崩溃

java - Lambda 表达式和 Optional 如何返回 String 值

c# - 使用 ForEach 语句修改列表的元素

java - 如何序列化 lambda?

java - 使用 ContentResolver 检索 SMS 的 NullPointerException 导致应用程序崩溃

java - 从父类(super class)继承字段还是在子类中写入字段?

c# - ExpressionTree 作为方法参数接受任何方法

java - 以下代码是否应该在 Java 1.8 下编译

JAVA 8 Stream 过滤器使用 Predicate 获取最新记录