【问题标题】:Where are Java 8 lambda expressions evaluated?Java 8 lambda 表达式在哪里求值?
【发布时间】:2017-11-24 07:35:21
【问题描述】:

是在我们编写它们的地方或在任何其他 Java 类中评估 lambda 表达式吗?

例如:

Stream<Student> absent =  students.values().stream().filter(s -> !s.present());

上面传递给过滤器方法的 lambda 表达式是否会在给定的类中立即执行,代码是在另一个类中编写的,还是在另一个类中执行,与编写代码相比,是否会花费更多的时间(以纳秒为单位) Java 8 之前的传统编码风格?

【问题讨论】:

  • 如果立即评估,s 的值是多少?

标签: java lambda


【解决方案1】:

当您编译源代码时,编译器将为您使用的 lambda 表达式插入一个 invokedynamic 字节码指令。实际的实现(在您的例子中是Predicate)将通过ASM 创建在运行时。当你运行它时,它甚至不会出现在硬盘上——这意味着类是在内存中生成的,Predicate 将没有 .class 文件。这是匿名类之间的一个很大的区别——例如,当你编译它时会生成一个class 文件。

如果您使用以下命令运行示例,您可以看到为Predicate 生成的文件:

-Djdk.internal.lambda.dumpProxyClasses=/Your/Path/Here

否则Eran's answer 是正确的,Streams 由终端操作驱动,如果不存在则不执行任何操作。您绝对应该阅读优秀的Holger's answer,了解更多有趣的差异。

【讨论】:

    【解决方案2】:

    在您的示例中传递给 filter 方法的 lambda 表达式的主体根本不会被执行,因为filter 是一个中间操作,它只会在以终端操作结束的Streams 中执行,比如collectforEach等……

    如果添加终端操作,比如将Stream的元素收集到List

    List<Student> absent =  students.values().stream().filter(s -> !s.present()).collect(Collectors.toList());
    

    将为您的Stream 的每个元素执行 lambda 表达式的主体,以便终端操作能够产生其输出。

    请注意,如果您将匿名类实例或 Predicate 接口的其他一些实现传递给 filter 方法而不是 lambda 表达式,则此行为不会改变。

    【讨论】:

      【解决方案3】:

      表达式是惰性求值的,这意味着它们只会在您实际尝试“终止”流时才被实际求值 - 即使用接受流但返回其他内容的操作,例如collectminmaxreduce 等。将流作为输入并返回流作为输出的操作通常是惰性的。

      【讨论】:

        【解决方案4】:

        Lambda 表达式本质上是具有单个方法的对象,因此只要调用该方法就会对它们进行评估。

        在您的特定情况下,它们从未被评估过。在您调用终止操作(collectfindAny 等)之前,Stream 不会计算表达式

        【讨论】:

          猜你喜欢
          • 2017-11-16
          • 1970-01-01
          • 2015-06-01
          • 2015-07-05
          • 1970-01-01
          • 1970-01-01
          • 2015-04-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多