Java 8的Lambda表达式在哪里被评估?

21

lambda表达式是在我们编写它们的位置还是在Java的其他任何类中进行评估?

例如:

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

传递给filter方法的上述lambda表达式将立即在编写代码的给定类中执行吗?还是在另一个类中执行并且需要比如果使用Java 8之前的传统代码风格编写代码需要更多的时间(以纳秒计)?


1
如果立即评估,s 的值将是什么? - Oliver Charlesworth
4个回答

18

当你编译源代码时,编译器会为使用的 lambda 表达式插入一个 invokedynamic 字节码指令。实际的实现(在你的情况下是一个 Predicate)将通过 ASM 在运行时创建。当你运行程序时,它甚至不会存在于硬盘上 - 这意味着类是在内存中生成的,没有 .class 文件来表示 Predicate。这是与匿名类之间的一个重要区别 - 后者在编译时会生成一个 class 文件。

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

-Djdk.internal.lambda.dumpProxyClasses=/Your/Path/Here
否则Eran的答案是正确的,Stream是由终端操作驱动的,如果没有这样的操作,就不会执行任何操作。你应该一定要阅读优秀的Holger的答案,了解更有趣的差异。

16
在您的示例中传递给过滤器方法的lambda表达式体将不会被执行,因为filter是一个中间操作,只有在以终端操作(例如collectforEach等)结束的Stream上才会执行。
如果您添加一个终端操作,例如将Stream的元素收集到List中:
List<Student> absent =  students.values().stream().filter(s -> !s.present()).collect(Collectors.toList());

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

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


5
表达式是惰性求值的,这意味着只有当你尝试“终止”流时,它们才会被实际评估——例如使用像 collect, min, max, reduce 等返回其他内容而不是流的操作。那些以流作为输入并返回流作为输出的操作通常都是惰性的。

2
Lambda表达式本质上是具有单个方法的对象,因此它们在调用该方法时进行评估。
在您的特定情况下,它们从未被评估。流不会评估表达式,直到您调用终止操作(例如collectfindAny等)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接