为什么lambda调用有2个堆栈帧?

Bog*_*mac 11 java lambda java-8

以下代码:

public static void main(String[] args) {
    Collections.singleton(1).stream().forEach(i -> new Exception().printStackTrace());
}
Run Code Online (Sandbox Code Playgroud)

打印:

java.lang.Exception
    at PrintLambdaStackTrace.lambda$main$0(PrintLambdaStackTrace.java:6)
    at PrintLambdaStackTrace$$Lambda$1/1831932724.accept(Unknown Source)
    at java.util.Collections$2.tryAdvance(Collections.java:4717)
    at java.util.Collections$2.forEachRemaining(Collections.java:4725)
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
    at PrintLambdaStackTrace.main(PrintLambdaStackTrace.java:6)
Run Code Online (Sandbox Code Playgroud)

lambda调用是如何实现的?为什么有2个堆栈帧?

Mar*_*nik 7

PrintLambdaStackTrace$$Lambda$1/1831932724.accept(Unknown Source)
Run Code Online (Sandbox Code Playgroud)

这是一个生成的类,它实现了所需的接口.它的accept方法只是一个存根,委托给一个在编译时生成并添加到PrintLambdaStackTrace类中的方法.此类是在lambda链接时生成的(第一次需要创建lambda实例).

PrintLambdaStackTrace.lambda$main$0(PrintLambdaStackTrace.java:6)
Run Code Online (Sandbox Code Playgroud)

这是实际实现lambda行为的方法.它属于这个PrintLambdaStackTrace类.

  • 是的,就静态生成的代码而言,开销较少.潜在地,在将来的实现中,运行时甚至不需要生成实现接口的完整类. (3认同)
  • @Bogdan Calmac:实现方法驻留在定义lambda表达式的类中,因此,自然可以访问周围的类.此外,您的磁盘/ jar文件中没有其他类文件... (2认同)
  • @Marko Topolnik:除了事实之外,区分内部类和普通类的唯一区别是对外部实例的引用,对于lambda类来说并不是必需的.如果你通过Reflection要求外型,它不会告诉你...... (2认同)
  • 好吧,所有的实例都在运行时发生(我以为这是理解的).JVM做到了,它涉及`invokedynamic`机制. (2认同)