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

Did*_*r L 15 java functional-programming predicate java-8

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

比方说我有这个POJO:

public class A {
    private Integer field;

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

    public Integer getField() {
        return field;
    }
}
Run Code Online (Sandbox Code Playgroud)

我想Stream根据以下值来过滤一个对象field:

    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))
            ...
Run Code Online (Sandbox Code Playgroud)

是否有方便的方法来生成方法的谓词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));
}
Run Code Online (Sandbox Code Playgroud)

并将其用作:

    Stream.of(new A(42), new A(1729), new A(42), new A(87539319), new A(null))
            .filter(isEqual(A::getField, someValue))
            ...
Run Code Online (Sandbox Code Playgroud)

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

Hol*_*ger 11

没有这样的内置工厂方法,您可以通过查看JFC中的所有用法Predicate轻松检查并查找"返回谓词的方法".除了Predicate本身的方法之外,只有Pattern.asPredicate()哪一个返回a Predicate.

在你要实施这种工厂方法之前,你应该问问自己这是否真的值得.是什么让你的lambda表达.filter(a -> Objects.equals(a.getField(), someValue))看起来很复杂,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()))
    …
Run Code Online (Sandbox Code Playgroud)

如果您仍想实施工厂方法并为创造性地使用已有的方法赢得价格,您可以使用:

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

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

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));
}
Run Code Online (Sandbox Code Playgroud)

这会影响测试常量是否null简化将在每次测试中执行的操作.所以它仍然不需要Objects.equals.请注意,Predicate.isEqual确实类似.

  • @PetrÚjezdský 这就是我的意思“当传递现有的‘Function’实现时”。但是,当直接传递方法引用时,编译器可以自由推断“Function&lt;A, Object&gt;”。您还可以使用 `(Function&lt;A, String&gt;)A::getName` 作为方法参数。不过,还是很麻烦。 (3认同)
  • @Roland:当您在每个函数求值中调用“Objects.equals”时,您将在每个函数求值中有效地重新检查“value”是否为“null”,尽管它不会改变。显然,如果*“value”为“null”,效果将是最大的,因为生成的函数“t -&gt; f.apply(t)==null”将比“Objects.equals”便宜得多。由于您可能会在不同的地方多次使用这种实用方法,因此效果会累积。如前所述,[`Predicate.isEqual` 的作用类似](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/function/Predicate.java第114章) (2认同)
  • @PetrÚjezdský 没有办法,因为编译器总是可以推断出“R”的“Object”。哪种类组合可以导致成功的“equals”测试,取决于“equals”的实际实现,两个类可以在没有公共基类(“Object”除外)的情况下兼容。当你改变`? 将 R` 扩展为 `R`,您可以在传递现有 `Function` 实现时限制参数。但对于传递 lambda 表达式或方法引用的预期用例,这没有帮助。简单地说,它像“equals”一样接受任意参数。 (2认同)