Java lambda 函数将如何编译?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16827262/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-01 00:06:23  来源:igfitidea点击:

How will Java lambda functions be compiled?

javalambdajava-8

提问by rtheunissen

Loop.times(5, () -> {
   System.out.println("looping");
});

Which of these would it effectively compile to?

它可以有效地编译为哪些?

for(int i = 0; i < 5; i++)
    System.out.println("looping");

or something like

或类似的东西

new CallableInterfaceImpl(){
    public void call(){
      for(int i = 0; i < 5; i++)
          System.out.println("looping");
    }
}.call();

So would it replace (kind of inline), or actually create an anonymous class?

那么它会替换(某种内联),还是实际上创建一个匿名类?

采纳答案by Jarandinor

The VM decides how to implement lambda, not a compiler.

VM 决定如何实现 lambda,而不是编译器。

See Translation strategysection in Translation of Lambda Expressions.

请参阅Lambda 表达式的翻译Translation strategy部分。

Instead of generating bytecode to create the object that implements the lambda expression (such as calling a constructor for an inner class), we describe a recipe for constructing the lambda, and delegate the actual construction to the language runtime. That recipe is encoded in the static and dynamic argument lists of an invokedynamic instruction.

我们没有生成字节码来创建实现 lambda 表达式的对象(例如调用内部类的构造函数),而是描述了构造 lambda 的方法,并将实际构造委托给语言运行时。该配方编码在调用动态指令的静态和动态参数列表中。

forconstruction from your example is most effective way in terms of simple compiling or perfomance (but the performance differences are very small, by the tests).

for就简单编译或性能而言,您的示例中的构造是最有效的方法(但通过测试,性能差异非常小)。

Addon:

插件

I created and disassemble two examples:

我创建并反汇编了两个示例:

for (String string: Arrays.asList("hello")) {
    System.out.println(string);
}

Disassembled bytecode, constants and other information:

反汇编的字节码、常量等信息:

Classfile LambdaCode.class
  Last modified 30.05.2013; size 771 bytes
  MD5 checksum 79bf2821b5a14485934e5cebb60c99d6
  Compiled from "LambdaCode.java"
public class test.lambda.LambdaCode
  SourceFile: "LambdaCode.java"
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #11.#22        //  java/lang/Object."<init>":()V
   #2 = Class              #23            //  java/lang/String
   #3 = String             #24            //  hello
   #4 = Methodref          #25.#26        //  java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
   #5 = InterfaceMethodref #27.#28        //  java/util/List.iterator:()Ljava/util/Iterator;
   #6 = InterfaceMethodref #29.#30        //  java/util/Iterator.hasNext:()Z
   #7 = InterfaceMethodref #29.#31        //  java/util/Iterator.next:()Ljava/lang/Object;
   #8 = Fieldref           #32.#33        //  java/lang/System.out:Ljava/io/PrintStream;
   #9 = Methodref          #34.#35        //  java/io/PrintStream.println:(Ljava/lang/String;)V
  #10 = Class              #36            //  test/lambda/LambdaCode
  #11 = Class              #37            //  java/lang/Object
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               main
  #17 = Utf8               ([Ljava/lang/String;)V
  #18 = Utf8               StackMapTable
  #19 = Class              #38            //  java/util/Iterator
  #20 = Utf8               SourceFile
  #21 = Utf8               LambdaCode.java
  #22 = NameAndType        #12:#13        //  "<init>":()V
  #23 = Utf8               java/lang/String
  #24 = Utf8               hello
  #25 = Class              #39            //  java/util/Arrays
  #26 = NameAndType        #40:#41        //  asList:([Ljava/lang/Object;)Ljava/util/List;
  #27 = Class              #42            //  java/util/List
  #28 = NameAndType        #43:#44        //  iterator:()Ljava/util/Iterator;
  #29 = Class              #38            //  java/util/Iterator
  #30 = NameAndType        #45:#46        //  hasNext:()Z
  #31 = NameAndType        #47:#48        //  next:()Ljava/lang/Object;
  #32 = Class              #49            //  java/lang/System
  #33 = NameAndType        #50:#51        //  out:Ljava/io/PrintStream;
  #34 = Class              #52            //  java/io/PrintStream
  #35 = NameAndType        #53:#54        //  println:(Ljava/lang/String;)V
  #36 = Utf8               test/lambda/LambdaCode
  #37 = Utf8               java/lang/Object
  #38 = Utf8               java/util/Iterator
  #39 = Utf8               java/util/Arrays
  #40 = Utf8               asList
  #41 = Utf8               ([Ljava/lang/Object;)Ljava/util/List;
  #42 = Utf8               java/util/List
  #43 = Utf8               iterator
  #44 = Utf8               ()Ljava/util/Iterator;
  #45 = Utf8               hasNext
  #46 = Utf8               ()Z
  #47 = Utf8               next
  #48 = Utf8               ()Ljava/lang/Object;
  #49 = Utf8               java/lang/System
  #50 = Utf8               out
  #51 = Utf8               Ljava/io/PrintStream;
  #52 = Utf8               java/io/PrintStream
  #53 = Utf8               println
  #54 = Utf8               (Ljava/lang/String;)V
{
  public test.lambda.LambdaCode();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return        
      LineNumberTable:
        line 15: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=3, args_size=1
         0: iconst_1      
         1: anewarray     #2                  // class java/lang/String
         4: dup           
         5: iconst_0      
         6: ldc           #3                  // String hello
         8: aastore       
         9: invokestatic  #4                  // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
        12: invokeinterface #5,  1            // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
        17: astore_1      
        18: aload_1       
        19: invokeinterface #6,  1            // InterfaceMethod java/util/Iterator.hasNext:()Z
        24: ifeq          47
        27: aload_1       
        28: invokeinterface #7,  1            // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
        33: checkcast     #2                  // class java/lang/String
        36: astore_2      
        37: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
        40: aload_2       
        41: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        44: goto          18
        47: return        
      LineNumberTable:
        line 35: 0
        line 36: 37
        line 37: 44
        line 38: 47
      StackMapTable: number_of_entries = 2
           frame_type = 252 /* append */
             offset_delta = 18
        locals = [ class java/util/Iterator ]
           frame_type = 250 /* chop */
          offset_delta = 28

}

and

Arrays.asList("hello").forEach(p -> {System.out.println(p);});

Disassembled bytecode, constants and other information:

反汇编的字节码、常量等信息:

Classfile LambdaCode.class
  Last modified 30.05.2013; size 1262 bytes
  MD5 checksum 4804e0a37b73141d5791cc39d51d649c
  Compiled from "LambdaCode.java"
public class test.lambda.LambdaCode
  SourceFile: "LambdaCode.java"
  InnerClasses:
       public static final #64= #63 of #70; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
  BootstrapMethods:
    0: #27 invokestatic java/lang/invoke/LambdaMetafactory.metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
      Method arguments:
        #28 invokeinterface java/util/function/Consumer.accept:(Ljava/lang/Object;)V
        #29 invokestatic test/lambda/LambdaCode.lambda##代码##:(Ljava/lang/String;)V
        #30 (Ljava/lang/String;)V
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #10.#21        //  java/lang/Object."<init>":()V
   #2 = Class              #22            //  java/lang/String
   #3 = String             #23            //  hello
   #4 = Methodref          #24.#25        //  java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
   #5 = InvokeDynamic      #0:#31         //  #0:lambda$:()Ljava/util/function/Consumer;
   #6 = InterfaceMethodref #32.#33        //  java/util/List.forEach:(Ljava/util/function/Consumer;)V
   #7 = Fieldref           #34.#35        //  java/lang/System.out:Ljava/io/PrintStream;
   #8 = Methodref          #36.#37        //  java/io/PrintStream.println:(Ljava/lang/String;)V
   #9 = Class              #38            //  test/lambda/LambdaCode
  #10 = Class              #39            //  java/lang/Object
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               main
  #16 = Utf8               ([Ljava/lang/String;)V
  #17 = Utf8               lambda##代码##
  #18 = Utf8               (Ljava/lang/String;)V
  #19 = Utf8               SourceFile
  #20 = Utf8               LambdaCode.java
  #21 = NameAndType        #11:#12        //  "<init>":()V
  #22 = Utf8               java/lang/String
  #23 = Utf8               hello
  #24 = Class              #40            //  java/util/Arrays
  #25 = NameAndType        #41:#42        //  asList:([Ljava/lang/Object;)Ljava/util/List;
  #26 = Utf8               BootstrapMethods
  #27 = MethodHandle       #6:#43         //  invokestatic java/lang/invoke/LambdaMetafactory.metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #28 = MethodHandle       #9:#44         //  invokeinterface java/util/function/Consumer.accept:(Ljava/lang/Object;)V
  #29 = MethodHandle       #6:#45         //  invokestatic test/lambda/LambdaCode.lambda##代码##:(Ljava/lang/String;)V
  #30 = MethodType         #18            //  (Ljava/lang/String;)V
  #31 = NameAndType        #46:#47        //  lambda$:()Ljava/util/function/Consumer;
  #32 = Class              #48            //  java/util/List
  #33 = NameAndType        #49:#50        //  forEach:(Ljava/util/function/Consumer;)V
  #34 = Class              #51            //  java/lang/System
  #35 = NameAndType        #52:#53        //  out:Ljava/io/PrintStream;
  #36 = Class              #54            //  java/io/PrintStream
  #37 = NameAndType        #55:#18        //  println:(Ljava/lang/String;)V
  #38 = Utf8               test/lambda/LambdaCode
  #39 = Utf8               java/lang/Object
  #40 = Utf8               java/util/Arrays
  #41 = Utf8               asList
  #42 = Utf8               ([Ljava/lang/Object;)Ljava/util/List;
  #43 = Methodref          #56.#57        //  java/lang/invoke/LambdaMetafactory.metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #44 = InterfaceMethodref #58.#59        //  java/util/function/Consumer.accept:(Ljava/lang/Object;)V
  #45 = Methodref          #9.#60         //  test/lambda/LambdaCode.lambda##代码##:(Ljava/lang/String;)V
  #46 = Utf8               lambda$
  #47 = Utf8               ()Ljava/util/function/Consumer;
  #48 = Utf8               java/util/List
  #49 = Utf8               forEach
  #50 = Utf8               (Ljava/util/function/Consumer;)V
  #51 = Utf8               java/lang/System
  #52 = Utf8               out
  #53 = Utf8               Ljava/io/PrintStream;
  #54 = Utf8               java/io/PrintStream
  #55 = Utf8               println
  #56 = Class              #61            //  java/lang/invoke/LambdaMetafactory
  #57 = NameAndType        #62:#66        //  metaFactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #58 = Class              #67            //  java/util/function/Consumer
  #59 = NameAndType        #68:#69        //  accept:(Ljava/lang/Object;)V
  #60 = NameAndType        #17:#18        //  lambda##代码##:(Ljava/lang/String;)V
  #61 = Utf8               java/lang/invoke/LambdaMetafactory
  #62 = Utf8               metaFactory
  #63 = Class              #71            //  java/lang/invoke/MethodHandles$Lookup
  #64 = Utf8               Lookup
  #65 = Utf8               InnerClasses
  #66 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
  #67 = Utf8               java/util/function/Consumer
  #68 = Utf8               accept
  #69 = Utf8               (Ljava/lang/Object;)V
  #70 = Class              #72            //  java/lang/invoke/MethodHandles
  #71 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #72 = Utf8               java/lang/invoke/MethodHandles
{
  public test.lambda.LambdaCode();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return        
      LineNumberTable:
        line 15: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=4, locals=1, args_size=1
         0: iconst_1      
         1: anewarray     #2                  // class java/lang/String
         4: dup           
         5: iconst_0      
         6: ldc           #3                  // String hello
         8: aastore       
         9: invokestatic  #4                  // Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List;
        12: invokedynamic #5,  0              // InvokeDynamic #0:lambda$:()Ljava/util/function/Consumer;
        17: invokeinterface #6,  2            // InterfaceMethod java/util/List.forEach:(Ljava/util/function/Consumer;)V
        22: return        
      LineNumberTable:
        line 28: 0
        line 38: 22
}

Compiler generated class-file is more complicated and larger (771b vs 1262b) for Lambda example.

对于 Lambda 示例,编译器生成的类文件更复杂、更大(771b 与 1262b)。

回答by Rams

Java compiler will generate synthetic methodsfor the code construct that is neither explicitly nor implicitly declared.

Java 编译器将为既不显式也不隐式声明的代码构造生成合成方法

As we are aware, lambda expression/function is an anonymous class method implementation for abstract method in functional interface and if we see the byte code of a compiled class file with lambda expression, Instead of creating a new object that will wrap the Lambda function, it uses the new INVOKEDYNAMICinstruction to dynamically link this call site to the actual Lambda function which is converted to private static synthetic lambda$0(Ljava/lang/String;)Vwhich will accept string as a parameter.

正如我们所知,lambda 表达式/函数是函数式接口中抽象方法的匿名类方法实现,如果我们看到带有 lambda 表达式的编译类文件的字节码,而不是创建一个将包装 Lambda 函数的新对象,它使用新的INVOKEDYNAMIC指令将此调用站点动态链接到实际的 Lambda 函数,该函数被转换为private static synthetic lambda$0(Ljava/lang/String;)V将接受字符串作为参数的函数

private static synthetic lambda$0(Ljava/lang/String;)V GETSTAIC java/lang/System.out: Ljava/io/PrintStream; ALOAD 0 INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V RETURN

private static synthetic lambda$0(Ljava/lang/String;)V GETSTAIC java/lang/System.out: Ljava/io/PrintStream; ALOAD 0 INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V RETURN

Example: list.forEach(x-> System.out.println(x));

例子: list.forEach(x-> System.out.println(x));

This lambda expression x-> System.out.println(x)is converted to private static synthetic block as mentioned above. But how this will be invoked for each element in the list when we run java Class?Refer the below byte code of lambda expression linkage as forEach accepts Consumer functional interface object.

x-> System.out.println(x)如上所述,此 lambda 表达式将转换为私有静态合成块。但是当我们运行 java Class 时,如何为列表中的每个元素调用 this 呢?请参阅以下 lambda 表达式链接的字节代码,因为 forEach 接受 Consumer 功能接口对象。

INVOKEDYNAMIC accept()Ljava/util/function/Consumer; [ java/lang/invoke/LambdaMetaFactory.metafactory(Ljava/lang/invokeMethodHandler$Lookup.Ljava/lang/invoke/CallSite.. //arguments (Ljava/lang/Object;)V //INVOKESTATIC com/<Classname>.lambda$)(Ljava/lang/String;)V, (Ljava/lang/String;)V ]

INVOKEDYNAMIC accept()Ljava/util/function/Consumer; [ java/lang/invoke/LambdaMetaFactory.metafactory(Ljava/lang/invokeMethodHandler$Lookup.Ljava/lang/invoke/CallSite.. //arguments (Ljava/lang/Object;)V //INVOKESTATIC com/<Classname>.lambda$)(Ljava/lang/String;)V, (Ljava/lang/String;)V ]

java.lang.invoke.LambdaMetaFactory: This class provides two forms of linkage methods:

java.lang.invoke.LambdaMetaFactory:该类提供两种形式的链接方法:

  1. A standard version (metafactory(MethodHandles.Lookup, String, MethodType, MethodType, MethodHandle, MethodType)) using an optimized protocol,
  2. An alternate version altMetafactory(MethodHandles.Lookup, String, MethodType, Object...)).
  1. 使用优化协议的标准版本(metafactory(MethodHandles.Lookup, String, MethodType, MethodType, MethodHandle, MethodType)),
  2. 替代版本 altMetafactory(MethodHandles.Lookup, String, MethodType, Object...))。

These linkage methods are designed to support the evaluation of lambda expressions and method references in the Java Language. For every lambda expressions or method reference in the source code, there is a target type which is a functional interface. Evaluating a lambda expression produces an object of its target type. The recommended mechanism for evaluating lambda expressions is to desugar the lambda body to a method, invoke an invokedynamic call site whose static argument list describes the sole method of the functional interface and the desugared implementation method, and returns an object (the lambda object) that implements the target type. Note(For method references, the implementation method is simply the referenced method; no desugaring is needed.)

这些链接方法旨在支持对 Java 语言中的 lambda 表达式和方法引用的评估。对于源代码中的每个 lambda 表达式或方法引用,都有一个目标类型,它是一个函数式接口。评估 lambda 表达式会生成其目标类型的对象。评估 lambda 表达式的推荐机制是将 lambda 体脱糖到一个方法,调用一个 invokedynamic 调用站点,其静态参数列表描述了函数接口的唯一方法和脱糖的实现方法,并返回一个对象(lambda 对象)实现目标类型。 注意(对于方法引用,实现方法只是被引用的方法;不需要脱糖。)