Java8 adds Lambda expressions, which greatly simplifies and compacts the code. How does Java8 implement Lambda expressions?
Take a look directly at a simple example of creating threads.
public class TestLambda { public static void main(String[] args) { new Thread(() -> System.out.print("thread")); } }
Execute the javac TestLambda.java compilation to generate the file TestLambda.class, and then analyze the class file with the javap command.
Execute javap-p TestLambda to display all classes and members.
// javap -p TestLambda Compiled from "TestLambda.java" public class TestLambda { public TestLambda(); public static void main(java.lang.String[]); private static void lambda$main$0(); // Compiled }
From the code above, you can see that the compiler generates a private static method based on the Lambda expression.
private static void lambda$main$0();
Print the details using the command javap-v-p TestLambda.
public class TestLambda minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #9.#24 // java/lang/Object."<init>":()V #2 = Class #25 // java/lang/Thread #3 = InvokeDynamic #0:#30 // #0:run:()Ljava/lang/Runnable; #4 = Methodref #2.#31 // java/lang/Thread."<init>":(Ljava/lang/Runnable;)V #5 = Fieldref #32.#33 // java/lang/System.out:Ljava/io/PrintStream; #6 = String #34 // thread #7 = Methodref #35.#36 // java/io/PrintStream.print:(Ljava/lang/String;)V #8 = Class #37 // TestLambda #9 = Class #38 // java/lang/Object #10 = Utf8 <init> #11 = Utf8 ()V #12 = Utf8 Code #13 = Utf8 LineNumberTable #14 = Utf8 LocalVariableTable #15 = Utf8 this #16 = Utf8 LTestLambda; #17 = Utf8 main #18 = Utf8 ([Ljava/lang/String;)V #19 = Utf8 args #20 = Utf8 [Ljava/lang/String; #21 = Utf8 lambda$main$0 #22 = Utf8 SourceFile #23 = Utf8 TestLambda.java #24 = NameAndType #10:#11 // "<init>":()V #25 = Utf8 java/lang/Thread #26 = Utf8 BootstrapMethods #27 = MethodHandle #6:#39 // 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; #28 = MethodType #11 // ()V #29 = MethodHandle #6:#40 // invokestatic TestLambda.lambda$main$0:()V #30 = NameAndType #41:#42 // run:()Ljava/lang/Runnable; #31 = NameAndType #10:#43 // "<init>":(Ljava/lang/Runnable;)V #32 = Class #44 // java/lang/System #33 = NameAndType #45:#46 // out:Ljava/io/PrintStream; #34 = Utf8 thread #35 = Class #47 // java/io/PrintStream #36 = NameAndType #48:#49 // print:(Ljava/lang/String;)V #37 = Utf8 TestLambda #38 = Utf8 java/lang/Object #39 = Methodref #50.#51 // 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; #40 = Methodref #8.#52 // TestLambda.lambda$main$0:()V #41 = Utf8 run #42 = Utf8 ()Ljava/lang/Runnable; #43 = Utf8 (Ljava/lang/Runnable;)V #44 = Utf8 java/lang/System #45 = Utf8 out #46 = Utf8 Ljava/io/PrintStream; #47 = Utf8 java/io/PrintStream #48 = Utf8 print #49 = Utf8 (Ljava/lang/String;)V #50 = Class #53 // java/lang/invoke/LambdaMetafactory #51 = NameAndType #54:#58 // 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; #52 = NameAndType #21:#11 // lambda$main$0:()V #53 = Utf8 java/lang/invoke/LambdaMetafactory #54 = Utf8 metafactory #55 = Class #60 // java/lang/invoke/MethodHandles$Lookup #56 = Utf8 Lookup #57 = Utf8 InnerClasses #58 = 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; #59 = Class #61 // java/lang/invoke/MethodHandles #60 = Utf8 java/lang/invoke/MethodHandles$Lookup #61 = Utf8 java/lang/invoke/MethodHandles { public TestLambda(); 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 1: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LTestLambda; public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 0: new #2 // class java/lang/Thread 3: dup 4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V 12: pop 13: return LineNumberTable: line 3: 0 line 4: 13 LocalVariableTable: Start Length Slot Name Signature 0 14 0 args [Ljava/lang/String; private static void lambda$main$0(); descriptor: ()V flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC Code: stack=2, locals=0, args_size=0 0: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #6 // String thread 5: invokevirtual #7 // Method java/io/PrintStream.print:(Ljava/lang/String;)V 8: return LineNumberTable: line 3: 0 } SourceFile: "TestLambda.java" InnerClasses: public static final #56= #55 of #59; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles BootstrapMethods: 0: #27 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: #28 ()V #29 invokestatic TestLambda.lambda$main$0:()V #28 ()V
As shown above: The decompiled byte code for a line of code that the main() method creates a thread is
0: new #2 // class java/lang/Thread 3: dup 4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V 12: pop 13: return
Find the third row of the constant pool based on 4: invokedynamic #3
#3 = InvokeDynamic #0:#30 // #0:run:()Ljava/lang/Runnable;
Where #0 points to the static factory method LambdaMetafactory.metafactory for BootstrapMethods:
BootstrapMethods: 0: #27 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: #28 ()V #29 invokestatic TestLambda.lambda$main$0:()V #28 ()V
LambdaMetafactory.metafactory uses asm to generate internal classes for Lambda expressions. The source code for the Metafactory method is as follows
public static CallSite metafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType) throws LambdaConversionException { AbstractValidatingLambdaMetafactory mf; mf = new InnerClassLambdaMetafactory(caller, invokedType, invokedName, samMethodType, implMethod, instantiatedMethodType, false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY); mf.validateMetafactoryArgs(); return mf.buildCallSite(); }
To view this internal class, run TestLambda with the parameters to generate the proxy class, which is java-Djdk.internal.lambda.dumpProxyClasses TestLambda.After running, it was found that a class file TestLambda$$Lambda$1.class was generated in the same directory.Execute javap-v-p TestLambda$$Lambda$1 to decompile this class.
final class TestLambda$$Lambda$1 implements java.lang.Runnable minor version: 0 major version: 52 flags: ACC_FINAL, ACC_SUPER, ACC_SYNTHETIC Constant pool: #1 = Utf8 TestLambda$$Lambda$1 #2 = Class #1 // TestLambda$$Lambda$1 #3 = Utf8 java/lang/Object #4 = Class #3 // java/lang/Object #5 = Utf8 java/lang/Runnable #6 = Class #5 // java/lang/Runnable #7 = Utf8 <init> #8 = Utf8 ()V #9 = NameAndType #7:#8 // "<init>":()V #10 = Methodref #4.#9 // java/lang/Object."<init>":()V #11 = Utf8 run #12 = Utf8 Ljava/lang/invoke/LambdaForm$Hidden; #13 = Utf8 TestLambda #14 = Class #13 // TestLambda #15 = Utf8 lambda$main$0 #16 = NameAndType #15:#8 // lambda$main$0:()V #17 = Methodref #14.#16 // TestLambda.lambda$main$0:()V #18 = Utf8 Code #19 = Utf8 RuntimeVisibleAnnotations { private TestLambda$$Lambda$1(); descriptor: ()V flags: ACC_PRIVATE Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #10 // Method java/lang/Object."<init>":()V 4: return public void run(); descriptor: ()V flags: ACC_PUBLIC Code: stack=0, locals=1, args_size=1 0: invokestatic #17 // Method TestLambda.lambda$main$0:()V 3: return RuntimeVisibleAnnotations: 0: #12() }
As you can see from above, the internal class TestLambda$$Lambda$1 implements java.lang.Runnable, the run() method, which is the method executed after the thread starts, and triggers the execution of TestLambda.lambda$main$0:(), which is the private static method generated when TestLambda is compiled.
summary
A Lambda expression that creates a thread adds a private static method to the class file after compilation. During running this class, an internal class is dynamically generated using the asm-operated bytecode technology. This internal class implements the Runnable interface and executes the thread. The internal thread method run actually calls the compilationPrivate static methods generated after translation to execute thread code.