在深入学习Lambda表达式之前,笔者也曾经认为Lambda就是匿名类的语法糖:
// Lambda Function<String, String> functionX = (String x) -> x; // 错误认知 Function<String, String> functionX = new Function<String, String>() { @Override public Void apply(String x) { return x; } }Lambda就是匿名类的语法糖这个认知是错误的。下面举一个例子,从源码和字节码的角度分析一下Lambda表达式编译和执行的整个流程。
public class Sample { public static void main(String[] args) throws Exception { Runnable runnable = () -> { System.out.println("Hello World!"); }; runnable.run(); String hello = "Hello "; Function<String, String> function = string -> hello + string; function.apply("Doge"); } }添加VM参数-Djdk.internal.lambda.dumpProxyClasses=.运行上面的Sample#main()方法,项目根目录动态生成了两个类如下:
import java.lang.invoke.LambdaForm.Hidden; // $FF: synthetic class final class Sample$$Lambda$14 implements Runnable { private Sample$$Lambda$14() { } @Hidden public void run() { Sample.lambda$main$0(); } } import java.lang.invoke.LambdaForm.Hidden; import java.util.function.Function; // $FF: synthetic class final class Sample$$Lambda$15 implements Function { private final String arg$1; private Sample$$Lambda$15(String var1) { this.arg$1 = var1; } private static Function get$Lambda(String var0) { return new Sample$$Lambda$15(var0); } @Hidden public Object apply(Object var1) { return Sample.lambda$main$1(this.arg$1, (String)var1); } }反查两个类的字节码,发现了类修饰符为final synthetic。接着直接看封闭类Sample的字节码:
public class club/throwable/Sample { <ClassVersion=52> <SourceFile=Sample.java> public Sample() { // <init> //()V <localVar:index=0 , name=this , desc=Lclub/throwable/Sample;, sig=null, start=L1, end=L2> L1 { aload0 // reference to self invokespecial java/lang/Object.<init>()V return } L2 { } } public static main(java.lang.String[] arg0) throws java/lang/Exception { //([Ljava/lang/String;)V <localVar:index=0 , name=args , desc=[Ljava/lang/String;, sig=null, start=L1, end=L2> <localVar:index=1 , name=runnable , desc=Lclub/throwable/Runnable;, sig=null, start=L3, end=L2> <localVar:index=2 , name=hello , desc=Ljava/lang/String;, sig=null, start=L4, end=L2> <localVar:index=3 , name=function , desc=Ljava/util/function/Function;, sig=Ljava/util/function/Function<Ljava/lang/String;Ljava/lang/String;>;, start=L5, end=L2> L1 { invokedynamic java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; : run()Lclub/throwable/Runnable; ()V club/throwable/Sample.lambda$main$0()V (6) ()V astore1 } L3 { aload1 invokeinterface club/throwable/Runnable.run()V } L6 { ldc "Hello " (java.lang.String) astore2 } L4 { aload2 invokedynamic java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; : apply(Ljava/lang/String;)Ljava/util/function/Function; (Ljava/lang/Object;)Ljava/lang/Object; club/throwable/Sample.lambda$main$1(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; (6) (Ljava/lang/String;)Ljava/lang/String; astore3 } L5 { aload3 ldc "Doge" (java.lang.String) invokeinterface java/util/function/Function.apply(Ljava/lang/Object;)Ljava/lang/Object; pop } L7 { return } L2 { } } private static synthetic lambda$main$1(java.lang.String arg0, java.lang.String arg1) { //(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; <localVar:index=0 , name=hello , desc=Ljava/lang/String;, sig=null, start=L1, end=L2> <localVar:index=1 , name=string , desc=Ljava/lang/String;, sig=null, start=L1, end=L2> L1 { new java/lang/StringBuilder dup invokespecial java/lang/StringBuilder.<init>()V aload0 // reference to arg0 invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder; aload1 invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lang/StringBuilder; invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String; areturn } L2 { } } private static synthetic lambda$main$0() { //()V L1 { getstatic java/lang/System.out:java.io.PrintStream ldc "Hello World!" (java.lang.String) invokevirtual java/io/PrintStream.println(Ljava/lang/String;)V } L2 { return } } // The following inner classes couldn't be decompiled: java/lang/invoke/MethodHandles$Lookup }上面的字节码已经被Bytecode-Viewer工具格式化过,符合于人的阅读习惯,从字节码的阅读,结合前面的分析大概可以得出下面的结论: