從最簡單的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)