Java8 增長了 Lambda 表達式,很大程度使代碼變的更加簡潔緊湊了,那麼 Java8 是如何實現 Lambda 表達式的呢?java
直接看一個簡單的建立線程的例子。ui
public class TestLambda { public static void main(String[] args) { new Thread(() -> System.out.print("thread")); } }
執行javac TestLambda.java
編譯生成文件TestLambda.class
,而後用javap
命令來分析這個class文件。
執行javap -p TestLambda
顯示全部類和成員。this
// 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(); // 編譯後生成的 }
由上面的代碼能夠看出編譯器根據 Lambda 表達式生成了一個私有靜態方法。線程
private static void lambda$main$0();
使用命令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
如上所示:main() 方法建立線程的一行代碼反編譯後的字節碼是code
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
根據4: invokedynamic #3
找到常量池第三行orm
#3 = InvokeDynamic #0:#30 // #0:run:()Ljava/lang/Runnable;
其中,#0
指向BootstrapMethods:
的靜態工廠方法LambdaMetafactory.metafactory
接口
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
會利用 asm 能夠爲 Lambda 表達式生成內部類,metafactory 方法源碼以下ip
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(); }
爲了查看這個內部類,加上生成代理類的參數運行 TestLambda,即java -Djdk.internal.lambda.dumpProxyClasses TestLambda
。運行結束後,發如今同級目錄生成了一個class文件TestLambda$$Lambda$1.class
。執行javap -v -p TestLambda$$Lambda$1
反編譯這個類。ci
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() }
由上可見,內部類TestLambda$$Lambda$1
實現了 java.lang.Runnable,run()
方法即線程啓動後執行的方法,它會觸發執行TestLambda.lambda$main$0:()
,即編譯TestLambda
時生成的私有靜態方法。
建立線程的 Lambda 表達式在編譯後會在 class 文件新增一個私有靜態方法,運行這個類的期間,會使用 asm 操做字節碼的技術,動態生成內部類,此內部類實現了 Runnable 接口,真正執行線程,線程方法 run 內部實際上就是調用了編譯後生成的私有靜態方法,從而執行線程代碼。