Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How will Java lambda functions be compiled?

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?

like image 887
rtheunissen Avatar asked May 30 '13 03:05

rtheunissen


People also ask

How does lambda expression work internally in Java?

Each lambda expression is mapped to a functional interface internally. The compiler decides which functional interface it should be mapped to from the context at the time of compiling the source code. Contrary to other functions in Java, you can define lambda functions that exist outside of the scope of any object.

How do I run a Java lambda function locally?

Debug Lambda Function Locally java . Right-click in your Eclipse code window, choose Debug As, and then choose AWS SAM Local. For this example, leave the Project and Template as they are. Choose Lambda Function in the Run as field.

What is needed for the compiler to understand lambda expressions?

To use lambda expression, you need to either create your own functional interface or use the pre defined functional interface provided by Java. An interface with only single abstract method is called functional interface(or Single Abstract method interface), for example: Runnable, callable, ActionListener etc.


2 Answers

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.

like image 191
Jarandinor Avatar answered Sep 25 '22 07:09

Jarandinor


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

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 INVOKEDYNAMIC instruction to dynamically link this call site to the actual Lambda function which is converted to private static synthetic lambda$0(Ljava/lang/String;)V which will accept string as a parameter.

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));

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.

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:

  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...)).

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.)

like image 20
Rams Avatar answered Sep 24 '22 07:09

Rams