Java8 教程

Java8 Lambda 实现原理

前面章节介绍了什么是 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 字节码文件:

Lambda 实现原理

其中,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 字节码文件:

Lambda 实现原理

注意,仅仅包含了 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实现方式

成功运行了上面代码后,目录下面的字节码如下图:

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实现方式");
   }

}
说说我的看法
全部评论(
没有评论
关于
本网站属于个人的非赢利性网站,转载的文章遵循原作者的版权声明,如果原文没有版权声明,请来信告知:hxstrive@outlook.com
公众号