我今天读了这篇关于 lambda 的文章:
http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood
文章建议,lambdas 没有实现为匿名内部类(由于性能)。
它给出了一个 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 是如何实现的?
表达式本身,假设您传递了一个实际的 lambda 表达式 而不是一个方法引用,被编译为一个单独的合成方法。除了预期功能接口(interface)的任何正式参数(例如,在 String
的情况下为单个 Consumer<String>
),它还将包含任何捕获值的参数。
在 lambda 表达式或方法引用出现的代码位置,invokedynamic
发出指令。第一次命中此指令时,将调用 LambdaMetafactory
上的 Bootstrap 方法。 .此引导方法将修复目标功能接口(interface)的实际实现,该接口(interface)委托(delegate)给目标方法,这就是返回的内容。目标方法是代表 lambda 主体的合成方法或使用 ::
提供的任何命名方法。运算符(operator)。当一个实现功能接口(interface)的类正在被创建时,这个过程被推迟了;它不会在编译时发生。
最后,运行时修补 invokedynamic
带有引导结果1 的站点,它实际上是对生成的委托(delegate)的构造函数调用,其中传递了任何捕获的值,包括(可能)调用目标2。这通过删除后续调用的引导过程来减轻性能影响。
1 参见 java.lang.invoke end of chapter "timing of linkage" ,由@Holger 提供。
2 对于没有捕获的 lambda,invokedynamic
指令通常会解析为可在后续调用期间重复使用的共享委托(delegate)实例,尽管这是一个实现细节。