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 内部实际上就是调用了编译后生成的私有静态方法,从而执行线程代码。