一、简要描述
- Lambda的底层实现原理
- Lambda表达式编译和运行过程
二、Lambda的底层实现原理
- Lambda表达式的本质
函数式接口的匿名子类的匿名对象
- 反编译:cfr-0.145.jar
反编译:LambdaMetafactory.metafactory()
跟踪调试,转储Lambda类:
jdk.internal.lambda.dumpProxyClasses
LambdaPrinciple$Lambda$1.class
- 结论
Lambda底层用匿名内部类实现:ASM技术
Lambda表达式是个语法糖
三、Lambda表达式编译和运行过程
- JVM参数:jdk.internal.lambda.dumpProxyClasses
- 命令:
java -Djdk.internal.lambda.dumpProxyClasses ClassName
- 转储得到内部类:
ClassName$$Lambda$1.class
- 反编译:
java -jar cfr-0.145.jar LambdaPrinciple.class --decodelambdas false
- 本质:函数式接口的匿名子类的匿名对象
Lambda表达式与函数接口的抽象函数格式一一对应
1、LambdaPrinciple 代码实现
package tech.flygo.lambda.demo4;
import java.util.Arrays;
import java.util.List;
/**
* Lambda表达式的底层实现
* 语法:
* (parameters) -> { statements; }
* 或
* (parameters) -> expression
*
* <p>
* JVM参数:jdk.internal.lambda.dumpProxyClasses
* 命令:java -Djdk.internal.lambda.dumpProxyClasses ClassName
* 转储得到内部类:ClassName$$Lambda$1.class
* 反编译:java -jar cfr-0.145.jar LambdaPrinciple.class --decodelambdas false
* <p>
* 本质:函数式接口的匿名子类的匿名对象
* Lambda表达式与函数接口的抽象函数格式一一对应
*/
public class LambdaPrinciple {
public static void main(String[] args) {
List<String> stringList = Arrays.asList("one", "two", "three");
// 通过lambda表达式实现元素遍历
stringList.forEach(s -> {
System.out.println(s);
});
}
}
2、cfr工具包解码Lambda代码实现
CFR解析包
cfr-0.145.jarhttps://www.yuque.com/attachments/yuque/0/2023/jar/1509175/1684030971197-5456d0f4-1c6a-45d0-bf96-009ee00cd9cd.jar
2.1、复制cfr工具包到class目录下
2.2、使用Java命令解码Lambda代码实现
- 进入class目录
- class和工具包同一级目录
- 使用java命令解码Lambda实现内容
java -jar cfr-0.145.jar LambdaPrinciple.class --decodelambdas false
2.3、解码出来的Lambda内容
/*
* Decompiled with CFR 0.145.
*/
package tech.flygo.lambda.demo4;
import java.io.PrintStream;
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> stringList = Arrays.asList("one", "two", "three");
stringList.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);
}
}
3、分析Lambda实现逻辑
3.1、LambdaMetafactory.metafactory()方法
从下面的源码可以看出,Java是严格遵循的面向对象原则,这里返回的是一个对象,而不是一个函数体。
3.2、调用InnerClassLambdaMetafactory
3.3、InnerClassLambdaMetafactory.buildCallSite()构造调用点
3.4、调用InnerClassLambdaMetafactory.spinInnerClass()
4、使用Java命令打开dumps调试模式
Java命令:
java -Djdk.internal.lambda.dumpProxyClasses ClassName
打开调试模式
4.1、进入class文件包文件的目录
特别注意:比如class文件的包路径为
tech.flygo.lambda.demo4
,则进入目录 tech的上一级目录执行java命令:
java -Djdk.internal.lambda.dumpProxyClasses tech.flygo.lambda.demo4.LambdaPrinciple
4.2、查看Lambda生成的匿名内部类
5、Java对动态语言的支持
使用javap查看class字节码:
javap -p -v LambdaPrinciple
Java7之后增加了动态指令
InvokeDynamic
,Java支持动态语言