是否有一种方便的方法来创建一个测试字段是否等于给定值的谓词?

Is there a convenience method to create a Predicate that tests if a field equals a given value?

我经常发现自己需要过滤 Stream 或使用谓词来检查给定字段是否具有给定值。

例如我有这个 POJO:

public class A {
    private Integer field;

    public A(final Integer field) {
        this.field = field;
    }

    public Integer getField() {
        return field;
    }
}

我想根据 field:

的值过滤 Stream 个对象
    final Integer someValue = 42;
    Stream.of(new A(42), new A(1729), new A(42), new A(87539319), new A(null))
            .filter(a -> Objects.equals(a.getField(), someValue))
            ...

是否有方便的方法来为 filter 方法生成谓词?我注意到有 Predicate.isEqual 但它不符合需要。

我可以很容易地写一个这样的:

public static <T,R> Predicate<T> isEqual(Function<? super T, ? extends R> f, R value) {
    return v -> Objects.equals(value, f.apply(v));
}

并将其用作:

    Stream.of(new A(42), new A(1729), new A(42), new A(87539319), new A(null))
            .filter(isEqual(A::getField, someValue))
            ...

但如果可能的话,我更愿意重用 JDK 中的现有方法。

没有这样的内置工厂方法,您可以通过查看 all usages of Predicate within the JFC and look for “Methods in … that return Predicate”. Besides the methods within Predicate itself, there is only Pattern.asPredicate() which returns a Predicate.

轻松检查

在你打算实现这样一个工厂方法之前,你应该问问自己这是否真的值得。 .filter(a -> Objects.equals(a.getField(), someValue)) 中的 lambda 表达式看起来复杂的原因是 Objects.equals 的使用,当您至少可以预测一个参数是否为 null 时,这是不必要的。由于此处 someValue 永远不会是 null,您可以简化表达式:

final Integer someValue = 42;
Stream.of(new A(42), new A(1729), new A(42), new A(87539319), new A(null))
    .filter(a -> someValue.equals(a.getField()))
    …

如果您仍然想实现工厂方法并通过创造性地使用已有的东西赢得价格,您可以使用:

public static <T,R> Predicate<T> isEqual(Function<? super T, ? extends R> f, R value) {
    return f.andThen(Predicate.isEqual(value)::test)::apply;
}

但是,对于生产代码,我宁愿推荐这样的实现:

public static <T,R> Predicate<T> isEqual(Function<? super T, ? extends R> f, R value) {
    return value==null? t -> f.apply(t)==null: t -> value.equals(f.apply(t));
}

这将测试常量是否为 null 以简化每个测试中将执行的操作。所以还是不需要Objects.equals。请注意,Predicate.isEqual 与此类似。