从最简单的lambda说起LambdaTest.java,代码如下:
package lambda.demo;
import java.util.function.Supplier;
public class LambdaTest {
public static void main(String[] args) {
Supplier 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."":()V
#2 = InvokeDynamic #0:#34 // #0:get:()Ljava/util/function/Supplier;
.......
#27 = Utf8 LambdaTest.java
#28 = NameAndType #9:#10 // "":()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)