You need to refer to SE8 15.27:
15.27.3. Type of a Lambda Expression
A lambda expression is compatible in an assignment context, invocation
context, or casting context with a target type T if T is a functional
interface type (§9.8) and the expression is congruent with the
function type of the ground target type derived from T.
Runtime handling use tricky things with invokedynamic
. Let examine some code:
import java.util.function.*;
class R implements Runnable {
public void run() { System.out.println("there"); }
}
public class L {
public static void execute(Runnable r) {
System.out.println(r.getClass());
r.run();
}
public static void main(String []a) {
execute(new R()); // subclass
execute(new Runnable() { // anonymous subclass
public void run() { System.out.println("elsewhere"); }
});
execute(() -> System.out.println("here")); // lambda
}
}
Execution gives:
> java L
class R
there
class L$1
elsewhere
class L$$Lambda$1/791452441
here
For the first two there is no surprise, the class of the object received by method execute
is (in the given order) R
(the subtype of Runnable
), L$1
(the anonymous subtype of Runnable
, and L$$Lambda$1/791452441
(the subtype of Runnable
constructed at run time from the lambda). Note that in the case of lambda there is no .class
file, the type is constructed at runtime by special construct. Let's examine the bytecode:
> javap -c -v L
Classfile /private/tmp/L.class
Last modified 1 août 2017; size 1234 bytes
MD5 checksum 9680a2bc143d25344979bae00fff3db7
Compiled from "L.java"
public class L
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #15.#28 // java/lang/Object."<init>":()V
#2 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #15.#31 // java/lang/Object.getClass:()Ljava/lang/Class;
#4 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/Object;)V
#5 = InterfaceMethodref #34.#35 // java/lang/Runnable.run:()V
#6 = Class #36 // R
#7 = Methodref #6.#28 // R."<init>":()V
#8 = Methodref #14.#37 // L.execute:(Ljava/lang/Runnable;)V
#9 = Class #38 // L$1
#10 = Methodref #9.#28 // L$1."<init>":()V
#11 = InvokeDynamic #0:#43 // #0:run:()Ljava/lang/Runnable;
#12 = String #44 // here
#13 = Methodref #32.#45 // java/io/PrintStream.println:(Ljava/lang/String;)V
#14 = Class #46 // L
#15 = Class #47 // java/lang/Object
#16 = Utf8 InnerClasses
#17 = Utf8 <init>
#18 = Utf8 ()V
#19 = Utf8 Code
#20 = Utf8 LineNumberTable
#21 = Utf8 execute
#22 = Utf8 (Ljava/lang/Runnable;)V
#23 = Utf8 main
#24 = Utf8 ([Ljava/lang/String;)V
#25 = Utf8 lambda$main$0
#26 = Utf8 SourceFile
#27 = Utf8 L.java
#28 = NameAndType #17:#18 // "<init>":()V
#29 = Class #48 // java/lang/System
#30 = NameAndType #49:#50 // out:Ljava/io/PrintStream;
#31 = NameAndType #51:#52 // getClass:()Ljava/lang/Class;
#32 = Class #53 // java/io/PrintStream
#33 = NameAndType #54:#55 // println:(Ljava/lang/Object;)V
#34 = Class #56 // java/lang/Runnable
#35 = NameAndType #57:#18 // run:()V
#36 = Utf8 R
#37 = NameAndType #21:#22 // execute:(Ljava/lang/Runnable;)V
#38 = Utf8 L$1
#39 = Utf8 BootstrapMethods
#40 = MethodHandle #6:#58 // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#41 = MethodType #18 // ()V
#42 = MethodHandle #6:#59 // invokestatic L.lambda$main$0:()V
#43 = NameAndType #57:#60 // run:()Ljava/lang/Runnable;
#44 = Utf8 here
#45 = NameAndType #54:#61 // println:(Ljava/lang/String;)V
#46 = Utf8 L
#47 = Utf8 java/lang/Object
#48 = Utf8 java/lang/System
#49 = Utf8 out
#50 = Utf8 Ljava/io/PrintStream;
#51 = Utf8 getClass
#52 = Utf8 ()Ljava/lang/Class;
#53 = Utf8 java/io/PrintStream
#54 = Utf8 println
#55 = Utf8 (Ljava/lang/Object;)V
#56 = Utf8 java/lang/Runnable
#57 = Utf8 run
#58 = Methodref #62.#63 // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#59 = Methodref #14.#64 // L.lambda$main$0:()V
#60 = Utf8 ()Ljava/lang/Runnable;
#61 = Utf8 (Ljava/lang/String;)V
#62 = Class #65 // java/lang/invoke/LambdaMetafactory
#63 = NameAndType #66:#69 // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#64 = NameAndType #25:#18 // lambda$main$0:()V
#65 = Utf8 java/lang/invoke/LambdaMetafactory
#66 = Utf8 metafactory
#67 = Class #71 // java/lang/invoke/MethodHandles$Lookup
#68 = Utf8 Lookup
#69 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#70 = Class #72 // java/lang/invoke/MethodHandles
#71 = Utf8 java/lang/invoke/MethodHandles$Lookup
#72 = Utf8 java/lang/invoke/MethodHandles
{
public L();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 9: 0
public static void execute(java.lang.Runnable);
descriptor: (Ljava/lang/Runnable;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #3 // Method java/lang/Object.getClass:()Ljava/lang/Class;
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: aload_0
11: invokeinterface #5, 1 // InterfaceMethod java/lang/Runnable.run:()V
16: return
LineNumberTable:
line 11: 0
line 12: 10
line 13: 16
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: new #6 // class R
3: dup
4: invokespecial #7 // Method R."<init>":()V
7: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
10: new #9 // class L$1
13: dup
14: invokespecial #10 // Method L$1."<init>":()V
17: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
20: invokedynamic #11, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
25: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
28: return
LineNumberTable:
line 15: 0
line 16: 10
line 19: 20
line 20: 28
}
SourceFile: "L.java"
InnerClasses:
static #9; //class L$1
public static final #68= #67 of #70; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #40 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#41 ()V
#42 invokestatic L.lambda$main$0:()V
#41 ()V
The first interesting part is the code of main
:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: new #6 // class R
3: dup
4: invokespecial #7 // Method R."<init>":()V
7: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
10: new #9 // class L$1
13: dup
14: invokespecial #10 // Method L$1."<init>":()V
17: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
20: invokedynamic #11, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
25: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
28: return
As you can observe there is no difference in between the explicit implementation of the interface or the anonymous one. The last only involves class naming trickery (L$1
), but both are used the same way, through invokestatic
.
Interesting case is the third (the lambda one) which involves invokedynamic
and then invokestatic
. Note that the invokestatic
calls the same method as in the two previous calls (the method run
).
Roughly, the first time the invokedynamic
is called a bootstrap method is called to construct a CallSite
(see CallSite in Java API) which will then be used further to execute the code of the lambda. See the bootstrap call here:
BootstrapMethods:
0: #40 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#41 ()V
#42 invokestatic L.lambda$main$0:()V
#41 ()V
and the code referred by the call site:
#41 = MethodType #18 // ()V
#42 = MethodHandle #6:#59 // invokestatic L.lambda$main$0:()V
#43 = NameAndType #57:#60 // run:()Ljava/lang/Runnable;
#44 = Utf8 here
#45 = NameAndType #54:#61 // println:(Ljava/lang/String;)V
#46 = Utf8 L
#47 = Utf8 java/lang/Object
#48 = Utf8 java/lang/System
#49 = Utf8 out
#50 = Utf8 Ljava/io/PrintStream;
#51 = Utf8 getClass
#52 = Utf8 ()Ljava/lang/Class;
#53 = Utf8 java/io/PrintStream
#54 = Utf8 println
#55 = Utf8 (Ljava/lang/Object;)V
#56 = Utf8 java/lang/Runnable
#57 = Utf8 run