【问题标题】:Predicates vs if statements谓词与 if 语句
【发布时间】:2017-03-16 09:28:00
【问题描述】:

我在一些项目中看到人们使用Predicates 而不是纯 if 语句,如下面的一个简单示例所示:

    int i = 5;
    // Option 1
    if (i == 5) {
        // Do something
        System.out.println("if statement");
    }

    // Option 2
    Predicate<Integer> predicate = integer -> integer == 5;
    if (predicate.test(i)) {
        // Do something
        System.out.println("predicate");
    }

首选Predicates 而非 if 语句有什么意义?

【问题讨论】:

  • 这在您想要重用谓词时很有用。举个例子:如果你想对不同的Lists 应用一个特定的过滤器怎么办?如果你想改变那个特定的过滤器怎么办?使用谓词是一种很好的方法,可以避免自己不得不在 100 多个地方更改代码。
  • 对于您给出的示例,谓词是完全没有必要的,只会让事情变得更复杂,没有充分的理由。
  • 从技术上讲,您将 if 语句用于两种用法。 Predicate 是一个用于生成变体条件的功能接口,通过它您可以在逻辑中测试这些变体条件。
  • 投票重新开放。这个问题的范围不是太广,到目前为止已经吸引了一些质量不错的答案。

标签: java if-statement lambda java-8 predicate


【解决方案1】:

使用谓词使您的代码更加灵活。

您可以编写一个评估Predicate 的条件,而不是编写一个总是检查i == 5 的条件,这允许您传递不同的Predicates 来实现不同的条件。

例如,Predicate 可以作为参数传递给方法:

public void someMethod (Predicate<Integer> predicate) {
    if(predicate.test(i)) {
        // do something
        System.out.println("predicate");
    }
    ...
}

这就是Streamfilter 方法的工作原理。

【讨论】:

  • 尽管如此,如果您不需要这种灵活性,那么您不应该使用Predicate&lt;&gt;。当然,在适当的情况下它是一个很棒的工具,但如果按照 OP 的示例使用它会使代码复杂化。
  • @cmaster 确实如此。
【解决方案2】:

对于您提供的 exact 示例,使用 Predicate 太过分了。编译器和运行时将创建:

  1. 一种方法(去糖谓词)
  2. 将实现 java.util.Predicate 的 .class
  3. 在 2 创建的类的实例

所有这些都与简单的 if 语句相比。

所有这一切都是为了一个无状态的谓词。如果您的谓词是有状态的,例如:

 Predicate<Integer> p = (Integer j) -> this.isJGood(j); // you are capturing "this"

那么每次你使用这个 Predicate 时,都会创建一个新的实例(至少在当前的 JVM 下)。

IMO 创建此类谓词的唯一可行选择当然是在多个地方重复使用它(例如作为参数传递给方法)。

【讨论】:

    【解决方案3】:

    使用 if 语句是检查二进制条件的最佳(阅读:最高性能)方法。

    switch 语句对于更复杂的情况可能会更快。

    PredicateFunction 的特殊形式。事实上,Java 语言架构师正在研究一种允许泛型原始类型的方法。这将使Predicate&lt;T&gt; 大致相当于Function&lt;T, boolean&gt;(模测试与应用方法名称)。

    如果一个函数(或方法)接受一个或多个函数作为参数,我们称之为高阶函数。我们说我们正在将 behaviour 传递给一个函数。这使我们能够创建强大的 API。

    String result = Match(arg).of(
            Case(isIn("-h", "--help"),    help()),
            Case(isIn("-v", "--version"), version()),
            Case($(),                     cmd -> "unknown command: " + cmd)
    );
    

    此示例取自Javaslang,这是一个用于 Java 8+ 中的对象函数式编程的库。

    免责声明:我是 Javaslang 的创建者。

    【讨论】:

      【解决方案4】:

      这是一个老问题,但我会试一试,因为我自己也在与它作斗争......

      为了原谅自己对谓词的使用,我制定了一个自我规则。

      我相信谓词在“逻辑点”的地方很有用 - NOTleaf |角落|结束 - a: graph |树 |直线,这将使逻辑点有效地成为“逻辑关节”。

      由于它是一个关节(又名节点),它有一个状态,一个可重用和可变的状态,作为达到目的的手段。

      在数据应该通过路径的流中,谓词很有用,因为它们授予一定程度的访问权限,同时保持流的完整性,这就是为什么最好的谓词 IMO 只是方法引用,最大限度地减少副作用。

      尽管 Predicate 最常见的形式是 newObject.equal(old),它本身就是一个 BiPredicate,但可以与具有副作用 lambda -&gt; lambda.equal(localCache) 的单个 Predicate 一起使用(所以这可能是 Only方法引用规则)。

      如果,逻辑作为输出/退出点指向不同的架构设计、组件或不是由您编写的代码,或者即使它是由您编写的,但其功能不同,那么if-else 是我要走的路。

      在反应式编程的情况下,谓词的另一个好处是多个订阅者可以使用同一个定义的逻辑门。

      但是,如果发布者的终点是一个单独的订阅者(如果我到达,这将与您的示例类似),那么使用 if-else 来完成逻辑会更好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-29
        • 1970-01-01
        • 2011-02-16
        • 2015-08-11
        相关资源
        最近更新 更多