前面章节介绍了什么是 Lambda 表达式,如何使用 Lambda 表达式,以及比较匿名内部类和 Lambda 表达式的区别,本章将更深入探讨一下 Lambda 表达式的实现原理。
假如我们通过匿名内部类启动一个线程,代码如下:
package com.hxstrive.jdk8.lambda_theory; /** * 匿名实现方式 * @author hxstrive.com */ public class AnonymousDemo { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("匿名实现方式"); } }).start(); } }
成功编译上面代码后,你将会看到如下两个 class 字节码文件:
其中,AnonymousDemo$1.class 是匿名内部类的字节码文件,AnonymousDemo.class 是 AnonymousDemo 类的字节码文件。
如果我们使用 Lambda 表达式实现上面同样的逻辑呢?代码如下:
package com.hxstrive.jdk8.lambda_theory; /** * Lambda实现方式 * @author hxstrive.com */ public class LambdaDemo { public static void main(String[] args) { new Thread(() -> { System.out.println("Lambda实现方式"); }).start(); } }
成功编译上面代码后,你将会看到如下 class 字节码文件:
注意,仅仅包含了 LambdaDemo.class 字节码文件,并没有生成 Lambda 表达式相关的字节码文件。为了更进一步,我们使用 JDK 自带的 javap 工具,对字节码进行反汇编,查看字节码指令。如下:
D:\jdk8-lambda\target\classes\com\hxstrive\jdk8\lambda_theory> javap -c -p LambdaDemo.class Compiled from "LambdaDemo.java" public class com.hxstrive.jdk8.lambda_theory.LambdaDemo { public com.hxstrive.jdk8.lambda_theory.LambdaDemo(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class java/lang/Thread 3: dup 4: invokedynamic #3, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 9: invokespecial #4 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V 12: invokevirtual #5 // Method java/lang/Thread.start:()V 15: return private static void lambda$main$0(); Code: 0: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #7 // String Lambda实现方式 5: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }
上述输出中,静态方法 lambda$main$0() 其实就是 Lambda 表达式编译后的结果。其中,lambda 字符串说明它是 Lambda 表达式,$main 说明它是 main() 方法中的 Lambda 表达式,$0 说明它是 main() 方法中第一个 Lambda 表达式。
那么,如何调用这个方法呢?其实 Lambda 表达式在运行的时候会生成一个内部类,为了验证是否生成内部类,可以在运行时加上 -Djdk.internal.lambda.dumpProxyClasses,加上这个参数后,运行时会将生成的内部类字节码输出到一个文件中。例如:
D:\jdk8-lambda\target\classes> java -Djdk.internal.lambda.dumpProxyClasses com.hxstrive.jdk8.lambda_theory.LambdaDemo Lambda实现方式
成功运行了上面代码后,目录下面的字节码如下图:
可知,上图中多了一个 LambdaDemo$$Lambda$1.class 字节码文件。我们继续使用 javap 工具,对字节码进行反汇编,查看字节码指令。如下:
D:\jdk8-lambda\target\classes\com\hxstrive\jdk8\lambda_theory> javap -c -p LambdaDemo$$Lambda$1.class final class com.hxstrive.jdk8.lambda_theory.LambdaDemo$$Lambda$1 implements java.lang.Runnable { private com.hxstrive.jdk8.lambda_theory.LambdaDemo$$Lambda$1(); Code: 0: aload_0 1: invokespecial #10 // Method java/lang/Object."<init>":()V 4: return public void run(); Code: 0: invokestatic #16 // Method com/hxstrive/jdk8/lambda_theory/LambdaDemo.lambda$main$0:()V 3: return }
从上可知,“Method com/hxstrive/jdk8/lambda_theory/LambdaDemo.lambda$main$0:()V” 这个匿名内部类的 run() 方法调用了 LambdaDemo 类中的静态方法 lambda$main$0,到这里应该明白 Lambda 表达式的原理了吧。
总结:
(1)将 Lambda 表达式编译成一个公共静态方法。
(2)运行代码时,将动态创建一个匿名内部类,该匿名内部类将调用(1)的公共静态方法。
上述示例实际的 Lambda 表达式代码如下:
package com.hxstrive.jdk8.lambda_theory; /** * Lambda实现方式原理 * @author hxstrive.com */ public class LambdaDemo2 { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { lambda$main$0(); } }).start(); } private static void lambda$main$0() { System.out.println("Lambda实现方式"); } }