从最简单的lambda说起LambdaTest.java,代码如下:
package lambda.demo; import java.util.function.Supplier; public class LambdaTest { public static void main(String[] args) { Supplier<Integer> supplier = () -> 1; System.out.println(supplier.get()); } }构建了一个lambda表达式赋值给Supplier函数接口,然后打印Supplier对象提供的值。
编译这个类,并用javap查看生成的字节码,部分字节码如下:
public class lambda.demo.LambdaTest SourceFile: "LambdaTest.java" InnerClasses: public static final #66= #65 of #69; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles BootstrapMethods: 0: #30 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: #31 ()Ljava/lang/Object; #32 invokestatic lambda/demo/LambdaTest.lambda$main$0:()Ljava/lang/Integer; #33 ()Ljava/lang/Integer; minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #8.#28 // java/lang/Object."<init>":()V #2 = InvokeDynamic #0:#34 // #0:get:()Ljava/util/function/Supplier; ....... #27 = Utf8 LambdaTest.java #28 = NameAndType #9:#10 // "<init>":()V #29 = Utf8 BootstrapMethods #30 = MethodHandle #6:#45 // 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; #31 = MethodType #46 // ()Ljava/lang/Object; #32 = MethodHandle #6:#47 // invokestatic lambda/demo/LambdaTest.lambda$main$0:()Ljava/lang/Integer; #33 = MethodType #25 // ()Ljava/lang/Integer; ...... { ...... public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: invokedynamic #2, 0 // InvokeDynamic #0:get:()Ljava/util/function/Supplier; 5: astore_1 6: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream; 9: aload_1 10: invokeinterface #4, 1 // InterfaceMethod java/util/function/Supplier.get:()Ljava/lang/Object; 15: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V 18: return ...... }可以看到第32行出现了invokedynamic指令,这正是lambda表达式在编译的时候生成的,顺着常量池索引#2找到CONSTANT_InvokeDynamic_info,由两部分组成:
(1)bootstrap方法限定符
每个invokedynamic指令出现的地方被称为动态调用点,每个动态调用点都对应一个bootstrap方法,bootstrap方法返回值为CallSite,此例中bootstrap方法为java.lang.invoke.LambdaMetafactory类的静态方法metafactory。
在方法限定符中还有三个参数分别是:
#31 ()Ljava/lang/Object; 表示函数式接口Supplier.get方法描述符
#32 invokestatic lambda/demo/LambdaTest.lambda$main$0:()Ljava/lang/Integer; lambda表达式编译时生成的匿名静态方法对应的描述符,这个匿名方法具体情况如下:
所在类:lambda.demo.LambdaTest
方法名:lambda$main$0
参数:无
返回值:java.lang.Integer
#33 ()Ljava/lang/Integer;具体的方法描述符,与#31相对应
(2)方法名和方法描述符:此处为Supplier get().
在这里可以看出:lambda在编译的时候生成匿名静态方法,同时还生成了invokedynamic指令,其bootstrap方法对应的动态调用点的MethodHandle,底层方法就是这个匿名方法。
当我们运行main函数时,JVM会将这些参数出栈,并传入LambdaMetafactory.metafactory方法中,以下是metafactory方法的定义:
public static CallSite metafactory(MethodHandles.Lookup caller, String invokedName, MethodType invokedType, MethodType samMethodType, MethodHandle implMethod, MethodType instantiatedMethodType)此处caller为lambda.demo.LambdaTest类
invokedName为get
invokedType为()Supplier
后面三个参数分别为方法限定符中的三个静态参数。
真正创建动态调用点的地方在InnerClassLambdaMetafactory.buildCallSite方法中,主要做了三件事:
(1)通过ASM工具生成名为lambda.demo.LambdaTest$$Lambda$1的类的字节码;注意ForwardingMethodGenerator.generate方法中以下代码:
visitMethodInsn(invocationOpcode(), implMethodClassName, implMethodName, implMethodDesc, implDefiningClass.isInterface());是将编译时生成的匿名方法对应的字节码加入到新生成的类中
(2)为lambda.demo.LambdaTest$$Lambda$1类创建实例,并转换为Supplier对应的MethodHandle;
(3)将(2)中的MethodHandler做为target生成动态调用点,并返回。
在这里可以看出:运行时通过ASM工具生成新类的字节码,包含了编译时的匿名方法,新类最终会被包装成一个动态调用点。执行invokedynamic指令,就会执行动态调用点的bootstrap方法,其底层方法就是编译时的匿名方法,也就是执行lambda表达式中的代码。
新生成的类的命名规则为:包名.类名$$Lambda$自增计数,当我们在debug或者抛出异常时看到这种类名就可以理解了:
Exception in thread "main" java.lang.RuntimeException at lambda.demo.LambdaTest.lambda$main$0(LambdaTest.java:13) at lambda.demo.LambdaTest$$Lambda$1/1283928880.accept(Unknown Source) at lambda.demo.LambdaTest.main(LambdaTest.java:15) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)