How will Java lambda functions be compiled?

2018-12-31 14:51发布

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?

1条回答
梦寄多情
2楼-- · 2018-12-31 15:32

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

See Translation strategy section in Translation of Lambda Expressions.

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.

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

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$0:(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$0
  #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$0:(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$0:(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$0:(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.

查看更多
登录 后发表回答