1.1 目标
了解Lambda的实现原理
我们现在已经会使用Lambda表达式了。现在同学们肯定很好奇Lambda是如何实现的,现在我们就来探究Lambda
表达式的底层实现原理。
@FunctionalInterfaceinterface Swimmable {public abstract void swimming();}
public class Demo04LambdaImpl {public static void main(String[] args) {// 匿名内部类在编译后会形成一个新的类.$goSwimming(new Swimmable() {@Overridepublic void swimming() {System.out.println("使用匿名内部类实现游泳");}});}public static void goSwimming(Swimmable swimmable) {swimmable.swimming();}}
我们可以看到匿名内部类会在编译后产生一个类: Demo04LambdaImpl$1.class

使用XJad反编译这个类,得到如下代码:
package com.itheima.demo01lambda;import java.io.PrintStream;// Referenced classes of package com.itheima.demo01lambda:// Swimmable, Demo04LambdaImplstatic class Demo04LambdaImpl$1 implements Swimmable {public void swimming(){System.out.println("使用匿名内部类实现游泳");}Demo04LambdaImpl$1() {}}
我们再来看看Lambda的效果,修改代码如下:
public class Demo04LambdaImpl {public static void main(String[] args) {goSwimming(() -> {System.out.println("Lambda游泳");});}public static void goSwimming(Swimmable swimmable) {swimmable.swimming();}}
运行程序,控制台可以得到预期的结果,但是并没有出现一个新的类,也就是说Lambda并没有在编译的时候产生一
个新的类。使用XJad对这个类进行反编译,发现XJad报错。使用了Lambda后XJad反编译工具无法反编译。我们使用
JDK自带的一个工具: javap ,对字节码进行反汇编,查看字节码指令。
在DOS命令行输入:
javap -c -p 文件名.class
-c:表示对代码进行反汇编
-p:显示所有类和成员
反汇编后效果如下:
C:\Users\>javap -c -p Demo04LambdaImpl.classCompiled from "Demo04LambdaImpl.java"public class com.itheima.demo01lambda.Demo04LambdaImpl {public com.itheima.demo01lambda.Demo04LambdaImpl();Code:0: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: invokedynamic #2, 0 // InvokeDynamic #0:swimming:()Lcom/itheima/demo01lambda/Swimmable;5: invokestatic #3 // Method goSwimming:(Lcom/itheima/demo01lambda/Swimmable;)V8: returnpublic static void goSwimming(com.itheima.demo01lambda.Swimmable);Code:0: aload_01: invokeinterface #4, 1 // InterfaceMethodcom/itheima/demo01lambda/Swimmable.swimming:()V6: returnprivate static void lambda$main$0();Code:0: getstatic #5 // Fieldjava/lang/System.out:Ljava/io/PrintStream;3: ldc #6 // String Lambda游泳5: invokevirtual #7 // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: return}
可以看到在类中多出了一个私有的静态方法 lambda$main$0 。这个方法里面放的是什么内容呢?我们通过断点调试
来看看:

可以确认 lambda$main$0 里面放的就是Lambda中的内容,我们可以这么理解 lambda$main$0 方法:
public class Demo04LambdaImpl {public static void main(String[] args) {...}private static void lambda$main$0() {System.out.println("Lambda游泳");}}
关于这个方法 lambda$main$0 的命名:以lambda开头,因为是在main()函数里使用了lambda表达式,所以带有
$main表示,因为是第一个,所以$0。
如何调用这个方法呢?其实Lambda在运行的时候会生成一个内部类,为了验证是否生成内部类,可以在运行时加
上 -Djdk.internal.lambda.dumpProxyClasses ,加上这个参数后,运行时会将生成的内部类class码输出到一个文
件中。使用java命令如下:
java -Djdk.internal.lambda.dumpProxyClasses 要运行的包名.类名
根据上面的格式,在命令行输入以下命令:
C:\Users>java -Djdk.internal.lambda.dumpProxyClasses
com.itheima.demo01lambda.Demo04LambdaImpl
Lambda游泳
执行完毕,可以看到生成一个新的类,效果如下:

反编译 Demo04LambdaImpl$$Lambda$1.class 这个字节码文件,内容如下:
// Referenced classes of package com.itheima.demo01lambda:// Swimmable, Demo04LambdaImplfinal class Demo04LambdaImpl$$Lambda$1 implements Swimmable {public void swimming(){Demo04LambdaImpl.lambda$main$0();}private Demo04LambdaImpl$$Lambda$1(){}}
可以看到这个匿名内部类实现了 Swimmable 接口,并且重写了 swimming 方法, swimming 方法调用
Demo04LambdaImpl.lambda$main$0() ,也就是调用Lambda中的内容。最后可以将Lambda理解为:
public class Demo04LambdaImpl {public static void main(String[] args) {goSwimming(new Swimmable() {public void swimming() {Demo04LambdaImpl.lambda$main$0();}});}private static void lambda$main$0() {System.out.println("Lambda表达式游泳");}public static void goSwimming(Swimmable swimmable) {swimmable.swimming();}}
1.2 小结
匿名内部类在编译的时候会一个class文件
Lambda在程序运行的时候形成一个类
1. 在类中新增一个方法,这个方法的方法体就是Lambda表达式中的代码
2. 还会形成一个匿名内部类,实现接口,重写抽象方法
3. 在接口的重写方法中会调用新生成的方法.
