11

The following code:

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

prints:

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)

How is the lambda invocation implemented? Why are there 2 stack frames?

Bogdan Calmac
  • 7,993
  • 6
  • 51
  • 64
  • That's a good question. I will be watching since I am interested in the answer myself. My guess is that the JVM creates an anonymous class at execution time. One stack is from the method used to create the class and the second from the class itself. But it is just a guess. – Sekkuar Jul 15 '15 at 16:35
  • 1
    See also http://stackoverflow.com/q/16827262/2711488 – Holger Jul 15 '15 at 16:49

1 Answers1

7
PrintLambdaStackTrace$$Lambda$1/1831932724.accept(Unknown Source)

This is a generated class which implements the required interface. Its accept method is just a stub, delegating to a method which was generated at compile time and added to the PrintLambdaStackTrace class. This class is generated at lambda linkage time (the first time a lambda instance needs to be created).

PrintLambdaStackTrace.lambda$main$0(PrintLambdaStackTrace.java:6)

This is the method which actually implements lambda's behavior. It belongs to the PrintLambdaStackTrace class.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • 1
    I wouldn’t call it *inner* class. It’s just a generated class. – Holger Jul 15 '15 at 16:45
  • I see. And what is the benefit of this approach compared to translating the lambda into an anonymous class? – Bogdan Calmac Jul 15 '15 at 16:48
  • 2
    @Bogdan Calmac: the implementation method resides in the class defining the lambda expression, hence, has naturally all access to the surrounding class. Further, no additional class file lying on your disk/in the jar file… – Holger Jul 15 '15 at 16:50
  • 3
    Yes, there's less overhead with in terms of statically generated code. Potentially, in future implementations, the runtime won't even need to generate a full-blown class which implements the interface. – Marko Topolnik Jul 15 '15 at 16:52
  • 1
    @Holger I don't think there's anything *wrong* with calling it inner, either. I do find it easier to understand in those terms. – Marko Topolnik Jul 15 '15 at 16:53
  • 2
    @Marko Topolnik: well, besides the fact, that the only thing which distinguishes an inner class from an ordinary class, the reference to an outer instance, is not mandatory for lambda classes. And if you ask for an outer type via Reflection, it won’t tell you… – Holger Jul 15 '15 at 16:58
  • @Holger An inner class may also be declared in a static context (as the case would be in this example). The true differentiation is that an inner class may not declare static members. See http://stackoverflow.com/questions/20468856/is-it-true-that-every-inner-class-requires-an-enclosing-instance – Marko Topolnik Jul 15 '15 at 17:00
  • 1
    @Bogdan Calmac: see also [“Why does lambda translation need generation of a static method?”](http://stackoverflow.com/a/30015130/2711488) – Holger Jul 15 '15 at 17:00
  • 1
    @Marko Topolnik: the naming is not always consistent, but in this case, it doesn’t matter whether you mean an inner class or a nested type as both is not correct in this case as the only thing which distinguishes a static member type from a top level class is an attribute in the class file which is not present in the lambda classes (and can’t be as it has to be symmetric, i.e. the outer class must declare all nested types, which is impossible for dynamically generated classes). – Holger Jul 15 '15 at 17:04
  • 1
    @Marko Topolnik: I was thinking of the JVM specification, which defines the `InnerClasses` attribute (note the name) to hold all nested types, regardless of whether the JLS calls them inner class or not. – Holger Jul 15 '15 at 17:16
  • @Holger That's exactly what I was reviewing, but all it says is that any `CONSTANT_Class_info` entry referring to a class which is not a member of a package must have corresponding data in the `InnerClasses` attribute. However, the class is not required to list all its inner classes in the `CONSTANT_Class_info` entry. It needs these entries only for the classes its bytecode refers to. – Marko Topolnik Jul 15 '15 at 17:17
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/83358/discussion-between-holger-and-marko-topolnik). – Holger Jul 15 '15 at 17:21
  • But said class is created during compile or execution time? – Sekkuar Jul 15 '15 at 17:38
  • 1
    @Sekkuar `This class is generated at lambda linkage time (the first time a lambda instance needs to be created).` – Marko Topolnik Jul 15 '15 at 17:42
  • And that happens during compilation or execution? :| Let me phrase that better: Who does that? JVM or java compiler? – Sekkuar Jul 15 '15 at 18:42
  • 2
    Well, all instantiation happens during runtime (I thought that was understood). The JVM does it and it involves the `invokedynamic` mechanism. – Marko Topolnik Jul 15 '15 at 18:45
  • Oh okay, I haven't paid enough attention to the word "instance", my bad. Thanks for the clarification. – Sekkuar Jul 16 '15 at 15:32