通过代码走读形式带大家了解下Lambda底层实现.
如果觉得麻烦,直接看结论。已总结原理。
Lambda的底层实现
- 编写代码
import java.util.Arrays;
import java.util.List;
/**
* JVM参数:jdk.internal.lambda.dumpProxyClasses
* 命令:java -Djdk.internal.lambda.dumpProxyClasses ClassName
* 转储得到内部类:ClassName$$Lambda$1.class
* 反编译:java -jar cfr-0.145.jar LambdaTest.class --decodelambdas false
* 本质:函数式接口的匿名子类的匿名对象
* Lambda表达式与函数式接口的抽象函数格式一一对应
*/
public class LambdaPrinciple {
public static void main(String[] args) {
List<String> strList = Arrays.asList("马", "士", "兵");
// 通过Lambda表达式实现元素遍历
strList.forEach(s -> {
System.out.println(s);
});
}
}
- 编译后生成的代码
// 在控制台执行java -jar cfr-0.152.jar LambdaPrinciple.class --decodelambdas false
// 生成如下代码
/*
* Decompiled with CFR 0.152.
*/
import java.lang.invoke.LambdaMetafactory;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class LambdaPrinciple {
public static void main(String[] args) {
List<String> strList = Arrays.asList("\u9a6c", "\u58eb", "\u5175");
strList.forEach((Consumer<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());
}
private static /* synthetic */ void lambda$main$0(String s) {
System.out.println(s);
}
}
// 可以看到forEach中的lambda被强转为Consumer实例, 因此可以看出LambdaMetafactory.metafactory生成了Consumer的实例对象
// 进入到方法LambdaMetafactory.metafactory
// 可以看到先new内部内对象元工厂InnerClassLambdaMetafactory,最后构造调用点mf.buildCallSite()
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();
}
// 构建内部类元工厂 mf = new InnerClassLambdaMetafactory
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
MethodType invokedType,
String samMethodName,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType,
boolean isSerializable,
Class<?>[] markerInterfaces,
MethodType[] additionalBridges)
throws LambdaConversionException {
super(caller, invokedType, samMethodName, samMethodType,
implMethod, instantiatedMethodType,
isSerializable, markerInterfaces, additionalBridges);
implMethodClassName = implDefiningClass.getName().replace('.', '/');
implMethodName = implInfo.getName();
implMethodDesc = implMethodType.toMethodDescriptorString();
implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial)
? implDefiningClass
: implMethodType.returnType();
constructorType = invokedType.changeReturnType(Void.TYPE);
lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
int parameterCount = invokedType.parameterCount();
// xxx 处理参数
}
// 构建调用点
CallSite buildCallSite() throws LambdaConversionException {
final Class<?> innerClass = spinInnerClass();
if (invokedType.parameterCount() == 0) {
// xxx处理调用参数
} else {
// xxxx
}
}
// spinInnerClass() 生成实现函数式接口的class文件,并返回类文件
private Class<?> spinInnerClass() throws LambdaConversionException {
String[] interfaces;
String samIntf = samBase.getName().replace('.', '/');
boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
if (markerInterfaces.length == 0) {
interfaces = new String[]{samIntf};
} else {
// Assure no duplicate interfaces (ClassFormatError)
Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);
itfs.add(samIntf);
for (Class<?> markerInterface : markerInterfaces) {
itfs.add(markerInterface.getName().replace('.', '/'));
accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
}
interfaces = itfs.toArray(new String[itfs.size()]);
}
cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
lambdaClassName, null,
JAVA_LANG_OBJECT, interfaces);
// Generate final fields to be filled in by constructor
for (int i = 0; i < argDescs.length; i++) {
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
argNames[i],
argDescs[i],
null, null);
fv.visitEnd();
}
generateConstructor();
if (invokedType.parameterCount() != 0) {
generateFactory();
}
// Forward the SAM method
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
samMethodType.toMethodDescriptorString(), null, null);
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
new ForwardingMethodGenerator(mv).generate(samMethodType);
// Forward the bridges
if (additionalBridges != null) {
for (MethodType mt : additionalBridges) {
mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
mt.toMethodDescriptorString(), null, null);
mv.visitAnnotation("Ljava/lang/invoke/LambdaForm$Hidden;", true);
new ForwardingMethodGenerator(mv).generate(mt);
}
}
if (isSerializable)
generateSerializationFriendlyMethods();
else if (accidentallySerializable)
generateSerializationHostileMethods();
cw.visitEnd();
// Define the generated class in this VM.
final byte[] classBytes = cw.toByteArray();
// If requested, dump out to a file for debugging purposes
if (dumper != null) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
@Override
public Void run() {
dumper.dumpClass(lambdaClassName, classBytes);
return null; }
}, null,
new FilePermission("<<ALL FILES>>", "read, write"),
// createDirectories may need it
new PropertyPermission("user.dir", "read"));
}
return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
}
// 查看dumper。
// For dumping generated classes to disk, for debugging purposes
private static final ProxyClassesDumper dumper;
static {
final String key = "jdk.internal.lambda.dumpProxyClasses";
String path = AccessController.doPrivileged(
new GetPropertyAction(key), null,
new PropertyPermission(key , "read"));
dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
}
// 反编译出内部类使用参数:jdk.internal.lambda.dumpProxyClasses
// 需要先进入到顶层包com的上级目录执行命令java -Djdk.internal.lambda.dumpProxyClasses com.example.learn.java8.lambda.demo04.LambdaPrinciple
// 我本地在E:\tmp, 包路径
java -Djdk.internal.lambda.dumpProxyClasses com.example.learn.java8.lambda.demo04.LambdaPrinciple
// 可以看到生成的匿名内部类LambdaPrinciple$$Lambda$1.class
// 反编译后看到匿名内部类LambdaPrinciple$$Lambda$1.class源码
java -jar cfr-0.152.jar LambdaPrinciple$$Lambda$1.class --decodelambdas false
// 生成的匿名内部类如下:
// 可以看到就是实现了Consumer接口的类
import com.example.learn.java8.lambda.demo04.LambdaPrinciple;
import java.lang.invoke.LambdaForm;
import java.util.function.Consumer;
final class LambdaPrinciple$$Lambda$1
implements Consumer {
private LambdaPrinciple$$Lambda$1() {
}
@LambdaForm.Hidden
public void accept(Object object) {
LambdaPrinciple.lambda$main$0((String)object);
}
}
- 反编译生成的代码
// 反编译后生成的字节码
// javac -p -v LambdaPrinciple.class
Classfile /E:/codes/practice_code/playit/java8/target/classes/com/example/learn/java8/lambda/demo04/LambdaPrinciple.class
Last modified 2023-8-10; size 1596 bytes
Compiled from "LambdaPrinciple.java"
public class com.example.learn.java8.lambda.demo04.LambdaPrinciple
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
// xxx 省略常量池部分
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=4, locals=2, args_size=1
0: iconst_3
1: anewarray #7 // class java/lang/String
4: dup
5: iconst_0
6: ldc #9 // String 马
8: aastore
9: dup
10: iconst_1
11: ldc #11 // String 士
13: aastore
14: dup
15: iconst_2
16: ldc #13 // String 兵
18: aastore
19: invokestatic #15 // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
22: astore_1
23: aload_1
24: invokedynamic #21, 0 // InvokeDynamic #0:accept:()Ljava/util/function/Consumer;
29: invokeinterface #25, 2 // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)V
34: return
LineNumberTable:
line 17: 0
line 19: 23
line 22: 34
LocalVariableTable:
Start Length Slot Name Signature
0 35 0 args [Ljava/lang/String;
23 12 1 strList Ljava/util/List;
LocalVariableTypeTable:
Start Length Slot Name Signature
23 12 1 strList Ljava/util/List<Ljava/lang/String;>;
private static void lambda$main$0(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #31 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #37 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
7: return
LineNumberTable:
line 20: 0
line 21: 7
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 s Ljava/lang/String;
}
SourceFile: "LambdaPrinciple.java"
BootstrapMethods:
0: #64 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:
#71 (Ljava/lang/Object;)V
#73 invokestatic com/example/learn/java8/lambda/demo04/LambdaPrinciple.lambda$main$0:(Ljava/lang/String;)V
#76 (Ljava/lang/String;)V
InnerClasses:
public static final #82= #78 of #80; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
结论(可直接跳到这里看原理即可)
最后,把第二步中的过程整理如下:
// 1. 编译原始类,同时lambda使用LambdaMetafactory.metafactory生成实现接口的对象实例
// 原始
strList.forEach(s -> {
System.out.println(s);
});
// 编译后生成下面两部分.
// 这部分通过Lambda元工厂(asm动态字节码技术)生成实现Consumer接口的实例
strList.forEach((Consumer<String>)LambdaMetafactory.metafactory(null, null, null, (Ljava/lang/Object;)V, lambda$main$0(java.lang.String ), (Ljava/lang/String;)V)());
}
// 生成上个步骤调用的实现方法:lambda$main$
private static /* synthetic */ void lambda$main$0(String s) {
System.out.println(s);
}
// 2. LambdaMetafactory.metafactory生成实现指定接口的匿名内部类实现
// 2.1 生成内部类Lambda元工厂
mf = new InnerClassLambdaMetafactory
// 2.1.1 创建cw(ClassWriter写字节码实例对象)
lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
// 2.1.2 处理参数信息
// 2.2 构造调用点
mf.buildCallSite();
// 2.2.1 创建内部类, 该类实现了函数式接口
innerClass = spinInnerClass();
// 2.2.1.1 生成构造器
generateConstructor();
// 2.2.1.2 转向实现接口的方法
new ForwardingMethodGenerator(mv).generate(samMethodType);
// 2.2.1.3 在VM中定义生成的class
return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
// 3. dumper处设置开启是否导出文件用来调试
// If requested, dump out to a file for debugging purposes
if (dumper != null) {
// ...
}
// 3.1 可以看到开启dump调试匿名内部类使用"jdk.internal.lambda.dumpProxyClasses"即可
private static final ProxyClassesDumper dumper;
static {
final String key = "jdk.internal.lambda.dumpProxyClasses";
String path = AccessController.doPrivileged(
new GetPropertyAction(key), null,
new PropertyPermission(key , "read"));
dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
}
// 4. 运行代码, 生成匿名内部类. 这里是用到动态字节码技术ASM(invokedynamic, 用来在运行时生成字节码)
// 4.1 进入到包根路径(类路径), 执行运行. 看到生成的匿名类LambdaPrinciple$$Lambda$1.class
java -Djdk.internal.lambda.dumpProxyClasses com.example.learn.java8.lambda.demo04.LambdaPrinciple
// 4.2 反编译看到匿名类为实现了Consumer接口的类. 也就是最开始的LambdaMetafactory.metafactory方法生成了该匿名类并加载到VM中
import com.example.learn.java8.lambda.demo04.LambdaPrinciple;
import java.lang.invoke.LambdaForm;
import java.util.function.Consumer;
final class LambdaPrinciple$$Lambda$1
implements Consumer {
private LambdaPrinciple$$Lambda$1() {
}
@LambdaForm.Hidden
public void accept(Object object) {
LambdaPrinciple.lambda$main$0((String)object);
}
}
再给一个官方的小例子. AbstractValidatingLambdaMetafactory
第44行
// invokedType
interface II<T> {
// samMethodName
Object foo(T x)
}
// samBase
interface JJ<R extends Number> extends II<R> { }
class CC { String impl(int i) { return "impl:"+i; }}
// targetClass
class X {
public static void main(String[] args) {
// 这里拿到对象后, 没有调用impl, 而是通过饮用方法, 将CC的实例对象的impl方法, 作为Lambda, 也就是入参为int i, 出参为String对象的Lambda. 即原始对CC实例的impl方法, 通过方法引用Forward到到JJ匿名内部类实现的Lambda对象上了。因此最后输入44, 调用的是CC的impl方法
JJ<Integer> iii = (new CC())::impl;
System.out.printf(">>> %s\n", iii.foo(44));
// 结果:>>> impl:44
}}