快速学习Lambda表达式

先看以下的一个示例:

@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 MainKaTeX parse error: Can't use function '$' in math mode at position 7: Lambda$̲1.class反编译MainLambda$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 实 现 了 P r i n t 接 口 , 并 且 o u t p u t 方 法 中 , 调 用 了 M a i n 类 中 的 私 有 静 态 方 法 l a m b d a 1实现了Print接口,并且output方法中,调用了Main类中的私有静态方法lambda 1PrintoutputMainlambdamain$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);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值