Java 8 lambdas是编译为内部类,方法还是别的什么?

时间:2014-10-08 12:46:57

标签: java lambda java-8

我今天读过这篇关于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

所以它不是同一个实例。它不是一些中心&#34; Lambda工厂&#34;例如..

如何实施lambdas?

4 个答案:

答案 0 :(得分:9)

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

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

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


1 请参阅java.lang.invoke end of chapter "timing of linkage",由@Holger提供。

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

答案 1 :(得分:2)

我问自己同样的问题并找到了this video,Brian Goetz的演讲。这是非常有用的介绍如何在java中实现lambda。

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

答案 2 :(得分:-1)

Java 8中的Lambda是所谓的功能接口,即具有一个default方法的匿名接口。

答案 3 :(得分:-2)

我不确定,但我相信,它只是编译给匿名的内部阶级。 Consumer<T>是一个界面,所以我想说你的例子几乎等于

    giveHello(new Consumer<String>() {

        @Override
        public void accept(String t) {
            System.out.println(t);
        }
    });

修改

经过一番研究,上述答案并不完整有效。 Lambda表达式可能会被翻译为匿名内部类,但不必(并且他们通常不会)。