【java8】一文带你明白lambda原理

通过代码走读形式带大家了解下Lambda底层实现.
如果觉得麻烦,直接看结论。已总结原理。

Lambda的底层实现

  1. 编写代码
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);  
        });  
    }  
}
  1. 编译后生成的代码
// 在控制台执行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);
    }
}
  1. 反编译生成的代码
// 反编译后生成的字节码
// 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
}}

CFR工具

参考:

  1. CFR反编译jar包
  2. 2023最新Java8:Lambda,Stream,响应式编程全系列讲解 一天学会别人0.5年学…
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值