Java 8 lambdas是编译为内部类,方法还是其他东西?

2022-09-03 07:48:31

我今天已经阅读了关于lambdas的这篇文章:

http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood

本文建议,lambdas 不是作为 anon 内部类实现的(由于性能原因)。它给出了一个示例,即 lambda 表达式可以编译为类的(静态)方法。

我尝试了一个非常简单的片段:

private void run() {
    System.out.println(this);
    giveHello(System.out::println);
}

private void giveHello(Consumer<String> consumer) {
    System.out.println(consumer);
    consumer.accept("hello");
}

输出为:

sample.Main@14ae5a5
sample.Main$$Lambda$1/168423058@4a574795
hello

所以它不是同一个实例。它也不是一些中央的“Lambda工厂”实例。

那么,lambda 是如何实现的呢?


答案 1

假设您传递的是实际的 lambda 表达式而不是方法引用,则表达式本身将编译为单独的综合方法。除了预期的功能接口的任何正式参数(例如,在的情况下是单个)之外,它还将包括任何捕获值的参数。StringConsumer<String>

在出现 lambda 表达式或方法引用的代码位置,将发出一条指令。第一次命中此指令时,将调用 上的引导方法。此引导方法将修复委托给目标方法的目标功能接口的实际实现,这就是返回的内容。目标方法是表示 lambda 主体的合成方法,或者是使用运算符提供的任何命名方法。在创建实现函数接口的类,该过程被推迟;它不会在编译时发生。invokedynamicLambdaMetafactory::

最后,运行时使用引导结果1 修补站点,这实际上是对生成的委托的构造函数调用,其中包含传入的任何捕获值,包括(可能)调用调用目标2。这通过删除后续调用的引导进程来减轻性能损失。invokedynamic


1 参见java.lang.invoke章节末尾“链接的时间”,由@Holger提供。

2 对于没有捕获的 lambda,指令通常会解析为可在后续调用期间重用的共享委托实例,尽管这是一个实现细节。invokedynamic


答案 2

我问了自己同样的问题,并找到了这个视频,布莱恩·戈茨(Brian Goetz)的演讲。这是关于如何在java中实现lambda的非常有用的介绍。

编辑(摘要):看了一会儿,所以这可能不完全正确。编译文件时,编译器会留下 lambda 应执行的操作的描述。然后,JRE 在运行代码时将决定如何实现 lambda。有几个选项,内联,方法引用,匿名类。然后,它将使用动态分配来分配实现。


推荐