【JAVA】Lambda执行原理

在我的想法里,Lambda长得很奇怪,虚拟机真的认识这玩意吗?还是说,Lambda经过编译后,脱掉了伪装的衣服,变成了大家熟知的方法?

对Lambda不熟悉的同学,可以先看我的另外两篇文章。


先看以下的一个示例:

//使用注解@FunctionalInterface来声明这是一个函数式接口
@FunctionalInterface
interface Print {
    void output(String str);
}

public class Main {

    private static void handle(String str, Print p) {
        p.output(str);
    }

    public static void main(String[] args) {
        handle("abc", str -> System.out.println(str));
    }
}

运行后,显然输出abc。

那么,这段代码被编译成什么样子了呢,我们使用javap -p Main.class查看编译后的类成员信息(-p显示所有的类和成员)

public class com.yang.testLambda.Main {
  public com.yang.testLambda.Main();
  private static void handle(java.lang.String, com.yang.testLambda.Print);
  public static void main(java.lang.String[]);
  private static void lambda$main$0(java.lang.String);
}

可以看到,多出来一个私有静态方法lambda$main$0

那这个静态方法,里面的内容又是什么呢?

继续使用javap -c -p Main.class(-c对代码进行反汇编)看看

public class com.yang.testLambda.Main {
  public com.yang.testLambda.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  private static void handle(java.lang.String, com.yang.testLambda.Print);
    Code:
       0: aload_1
       1: aload_0
       2: invokeinterface #2,  2            // InterfaceMethod com/yang/testLambda/Print.output:(Ljava/lang/String;)V
       7: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #3                  // String abc
       2: invokedynamic #4,  0              // InvokeDynamic #0:output:()Lcom/yang/testLambda/Print;
       7: invokestatic  #5                  // Method handle:(Ljava/lang/String;Lcom/yang/testLambda/Print;)V
      10: return

  private static void lambda$main$0(java.lang.String);
    Code:
       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: return
}

只看lambda$main$0方法,发现里面是

new PrintStream("abc").println();

也就是

System.out.println("abc")

说明,生成的私有静态方法里面的内容就是lambda表达式里面的主要内容。

那么,这个私有静态方法,是何时何地被谁调用的呢?

现在需要使用javac Main.java编译成Main.class文件,之后使用java -Djdk.internal.lambda.dumpProxyClasses Main来运行,并会将运行过程中产生的内部类输出出来。

运行第一个命令后,会产生Main.class和Print.class文件

运行第二个命令后,会额外产生Main$$Lambda$1.class文件

使用javap -c -p Main$$Lambda$1.class反编译Main$$Lambda$1.class文件,会得到

final class Main$$Lambda$1 implements Print {
  private Main$$Lambda$1();
    Code:
       0: aload_0
       1: invokespecial #10                 // Method java/lang/Object."<init>":()V
       4: return

  public void output(java.lang.String);
    Code:
       0: aload_1
       1: invokestatic  #18                 // Method Main.lambda$main$0:(Ljava/lang/String;)V
       4: return
}

发现Main$$Lambda$1实现了Print接口,并且output方法中,调用了Main类中的私有静态方法lambda$main$0

那么,该内部类又是何时何地被谁调用的呢?

而在一开始我们使用javap -c-p Main.class时,其中主方法是:

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #3                  // String abc
       2: invokedynamic #4,  0              // InvokeDynamic #0:output:()Lcom/yang/testLambda/Print;
       7: invokestatic  #5                  // Method handle:(Ljava/lang/String;Lcom/yang/testLambda/Print;)V
      10: return

可以看得出这边使用了invokedynamic调用了函数式接口,可以粗略的认为这里实例化了Print的实现类(其实具体的逻辑太挺复杂,这里直接简化了),就是内部类Main$$Lambda$1,然后调用静态方法handle,这个方法接收一个字符串和Print实现类实例。

那么,一开始的lambda表达式,可以改写成这样的形式:

interface Print {
    void output(String str);
}

public class Main {

    private static void handle(String str, Print p) {
        p.output(str);
    }

    //编译后生成的私有静态方法,方法内容就是lambda里的内容
    private static void lambda$main$0(String str) {
        System.out.println(str);
    }

    //运行时生成的内部类,实现函数式接口,实现方法中调用私有静态方法
    final class Main$$Lambda$1 implements Print {

        @Override
        public void output(String str) {
            lambda$main$0(str);
        }
    }

    public static void main(String[] args) {
        Print print = new Main().new Main$$Lambda$1();
        handle("abc", print);
    }
}

到这里,lambda表达式的执行原理,已经粗浅的解释完毕了。

发布了177 篇原创文章 · 获赞 583 · 访问量 115万+

猜你喜欢

转载自blog.csdn.net/qq_33591903/article/details/105270336