前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中的Lambda是如何实现的

Java中的Lambda是如何实现的

作者头像
KINGYT
发布2023-03-15 13:55:39
1K0
发布2023-03-15 13:55:39
举报

首先来看段Java代码

代码语言:javascript
复制
import java.util.function.Consumer;
public class Test {
  public static void main(String[] args) {
    String s1 = "hello";
    Consumer<String> c =
        s2 -> {
          System.out.println(s1);
          System.out.println(s2);
        };
    c.accept("world");
  }
}

看下其对应的字节码

代码语言:javascript
复制
➜  javac Test.java                                  
➜  javap -p -v Test                                 
...
{
  ...
  public static void main(java.lang.String[]);
    ...
         0: ldc           #2                  // String hello
         2: astore_1
         3: aload_1
   4: invokedynamic #3,  0              // InvokeDynamic #0:accept:(Ljava/lang/String;)Ljava/util/function/Consumer;
         9: astore_2
        10: aload_2
        11: ldc           #4                  // String world
  13: invokeinterface #5,  2            // InterfaceMethod java/util/function/Consumer.accept:(Ljava/lang/Object;)V
        18: return
    ...


  private static void lambdamain0(java.lang.String, java.lang.String);
    ...
         0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: aload_0
    4: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         7: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
        10: aload_1
    11: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        14: return
    ...
}
...

在上面的字节码中,我们可以看到一个名为 lambdamain0 的方法,该方法是在编译阶段自动生成的,其对应于示例源码中的lambda方法体。

在main方法的字节码中,invokedynamic是整个lambda实现的关键,不过由于该字节码在JVM中的实现逻辑非常复杂,在这里我们就不看具体代码了,只说下大致思路。

该字节码的最终目的是为了创建一个对象,且该对象要实现java.util.function.Consumer接口,这样这个对象才可以赋值给上面示例源码中的 Consumer<String> c 变量。

那这个对象对应的类是哪里来的呢?对,也是动态生成的。

JVM在执行invokedynamic字节码时,会根据class文件中提供的各种信息,调用java.lang.invoke.LambdaMetafactory.metafactory方法来动态生成这个类。

该类的生成结果我们可以通过下面的方式看下

➜ java -Djdk.internal.lambda.dumpProxyClasses Test hello world

执行完上面的命令后,会在当前目录生成一个名为 Test$Lambda1.class 的文件,该文件的内容就是invokedynamic字节码动态生成的类,我们反编译看下

代码语言:javascript
复制
import java.lang.invoke.LambdaForm.Hidden;
import java.util.function.Consumer;


// $FF: synthetic class
final class Test$Lambda1 implements Consumer {
  private final String arg$1;


  private Test$Lambda1(String var1) {
    this.arg$1 = var1;
  }


  private static Consumer get$Lambda(String var0) {
    return new Test$Lambda1(var0);
  }


  @Hidden
  public void accept(Object var1) {
    Test.lambdamain0(this.arg
  }
}

恩,正如我们所想,该类确实是实现了java.util.function.Consumer接口。

在该类生成完毕之后,invokedynamic字节码会调用其getLambda方法来创建一个TestLambda1实例,该实例创建成功,也意味着invokedynamic字节码执行完毕。

该实例接着被赋值给了Consumer<String> c 变量,之后调用其accept方法,而在accept方法中又调用了编译阶段生成的Test.lambda

之后的流程就是一般的Java执行流程了,在此不做过多介绍。

依据上面的示例,我们再来总结下lambda是如何实现的:

在编译阶段,javac会自动生成一个lambdamain0方法,该方法对应了lambda的方法体。

在运行阶段,当执行invokedynamic字节码时,JVM会根据编译阶段提供的各种信息,调用java.lang.invoke.LambdaMetafactory.metafactory方法动态生成Test$Lambda1类,该类实现了java.util.function.Consumer接口,且在其accept方法中直接调用了编译阶段生成的lambdamain0方法。

当TestLambda当Test类生成完毕后,invokedynamic字节码会调用其get$Lambda方法,生成一个TestLambda

该实例创建完毕也意味着invokedynamic字节码执行完毕。

该实例创建完毕之后,赋值给了示例源码中的Consumer<String> c变量,然后调用其accept方法,传入world字符串。

在Test$Lambda1类中的accept方法中,其直接调用了编译阶段生成的Test.lambdamain0方法,传入参数变量为arg

Test.lambdamain0方法最终执行了lambda的方法体代码。

至此,整个流程结束。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-02-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 卯时卯刻 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档