What is a Java 8 Lambda Expression Compiled to?
The current draft of the Java 8 Language Specification states (chapter 15.27.4)
The value of a lambda expression is a reference to an instance of a class with the following properties:
- The class implements the targeted functional interface and, if the target type is an intersection type, every other interface element of the intersection.
- The class declares a method that overrides the functional interface supertype's abstract methods (and, potentially, some other methods of its superinterfaces).
- The method's parameter types, return type, and thrown types are given by the interface's function type.
- The method's body has the effect of evaluating the lambda body, if it is an expression, or of executing the lambda body, if it is a block; if a result is expected, it is returned from the method.
- The class overrides no other methods of the interface or interfaces mentioned above, except that it may override methods of the
Object
class.
Note that the JLS doesn't say anything about how the code should be compiled except that the byte code should support the specification above.
As such, the object returned by the lambda expression
x -> System.out.print(x);
will be an instance of a class that follows the above rules.
Given your comment that
consumer.getClass()
returns the following class
Example$$Lambda$1/1072591677
it seems that it is generating a proxy-like class specific for lambda expressions.
See here:
Related videos on Youtube
Comments
-
ams over 1 year
Consider the following Java 8 snippet.
public static void main(String[] args) { List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5); Consumer<Integer> consumer = x -> System.out.print(x); integers.forEach(consumer); }
What is
Consumer<Integer> consumer = x -> System.out.print(x)
getting compiled to?I understand that Lambdas are not implemented as anonymous inner classes. However
Consumer<Integer>
is an interface thereforex -> System.out.print(x)
must be producing an object of some kind but it is not clear what kind of object is being produced.Is there some new type of object in Java 8 to represent a lambda expression?
Update Here is the decompiled program the program was complied with the eclipse java 8 complier and the output below is from eclipse when you open a class file.
It looks like that the lambda expression is getting turned into a static method on the class that contains the lambda expression
private static synthetic void lambda$0(java.lang.Integer x);
// Compiled from Example.java (version 1.8 : 52.0, super bit) public class Example { // Method descriptor #6 ()V // Stack: 1, Locals: 1 public Example(); 0 aload_0 [this] 1 invokespecial java.lang.Object() [8] 4 return Line numbers: [pc: 0, line: 7] Local variable table: [pc: 0, pc: 5] local: this index: 0 type: Example // Method descriptor #15 ([Ljava/lang/String;)V // Stack: 4, Locals: 3 public static void main(java.lang.String[] args); 0 iconst_5 1 anewarray java.lang.Integer [16] 4 dup 5 iconst_0 6 iconst_1 7 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [18] 10 aastore 11 dup 12 iconst_1 13 iconst_2 14 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [18] 17 aastore 18 dup 19 iconst_2 20 iconst_3 21 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [18] 24 aastore 25 dup 26 iconst_3 27 iconst_4 28 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [18] 31 aastore 32 dup 33 iconst_4 34 iconst_5 35 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [18] 38 aastore 39 invokestatic java.util.Arrays.asList(java.lang.Object[]) : java.util.List [22] 42 astore_1 [integers] 43 invokedynamic 0 accept() : java.util.function.Consumer [31] 48 astore_2 [consumer] 49 getstatic java.lang.System.out : java.io.PrintStream [32] 52 aload_2 [consumer] 53 invokevirtual java.lang.Object.getClass() : java.lang.Class [38] 56 invokevirtual java.lang.Class.getCanonicalName() : java.lang.String [42] 59 invokevirtual java.io.PrintStream.println(java.lang.String) : void [48] 62 getstatic java.lang.System.out : java.io.PrintStream [32] 65 aload_2 [consumer] 66 invokevirtual java.lang.Object.getClass() : java.lang.Class [38] 69 invokevirtual java.lang.Class.getTypeName() : java.lang.String [54] 72 invokevirtual java.io.PrintStream.println(java.lang.String) : void [48] 75 aload_1 [integers] 76 aload_2 [consumer] 77 invokeinterface java.util.List.forEach(java.util.function.Consumer) : void [57] [nargs: 2] 82 return Line numbers: [pc: 0, line: 10] [pc: 43, line: 12] [pc: 49, line: 14] [pc: 62, line: 15] [pc: 75, line: 17] [pc: 82, line: 18] Local variable table: [pc: 0, pc: 83] local: args index: 0 type: java.lang.String[] [pc: 43, pc: 83] local: integers index: 1 type: java.util.List [pc: 49, pc: 83] local: consumer index: 2 type: java.util.function.Consumer Local variable type table: [pc: 43, pc: 83] local: integers index: 1 type: java.util.List<java.lang.Integer> [pc: 49, pc: 83] local: consumer index: 2 type: java.util.function.Consumer<java.lang.Integer> // Method descriptor #73 (Ljava/lang/Integer;)V // Stack: 2, Locals: 1 private static synthetic void lambda$0(java.lang.Integer x); 0 getstatic java.lang.System.out : java.io.PrintStream [32] 3 aload_0 [x] 4 invokevirtual java.io.PrintStream.print(java.lang.Object) : void [74] 7 return Line numbers: [pc: 0, line: 12] Local variable table: [pc: 0, pc: 8] local: x index: 0 type: java.lang.Integer Inner classes: [inner class info: #96 java/lang/invoke/MethodHandles$Lookup, outer class info: #98 java/lang/invoke/MethodHandles inner name: #100 Lookup, accessflags: 25 public static final] Bootstrap methods: 0 : # 89 arguments: {#90,#93,#94} }
-
Sotirios Delimanolis about 10 yearsTry
consumer.getClass()
for hints, then read this and then the JLS. -
ams about 10 years@SotiriosDelimanolis
consumer.getClass()
outputs Example$$Lambda$1/1072591677 -
Luiggi Mendoza about 10 yearsUse
javap -c YourClass.class
to check how the code gets compiled. -
Sotirios Delimanolis about 10 yearsI don't have Java 8 on my current environment, can you
javap -v -c ..
so we can get the constants as well?invokedynamic 0 accept()
is what should be getting the instance, but0
is referencing something in the constant pool.
-
-
Victor Grazi over 9 yearsNote that in the Java 8 release implementation, Lambdas do not compile into separate class files.
-
Sotirios Delimanolis over 9 years@victor Right, these are all runtime generated classes.
-
Hakanai over 9 yearsNow they're not even classes. If you call getClass() you'll just get an exception.
-
Sotirios Delimanolis over 9 years@Trejkaz That doesn't seem right. Can you post an example?
-
Hakanai over 9 yearsOur code is too weavy to just post, but I'm on a line in the debugger which just has: primaryType = config.getDelegate().getClass();. config.getDelegate() returns a lambda, and config.getDelegate().getClass() throws the exception. Funnily enough, OurInterface.class.isInstance(config.getDelegate()) returns true. What I'm actually trying to do is find which interfaces the object implements, which is rather harder when you can't access the Class.
-
Sotirios Delimanolis over 9 years@Trejkaz You'd be better off asking a new question with relevant details and reproducible code. I can't really help here without seeing the bigger picture.
-
Hakanai over 9 yearsYeah, I didn't really have a question about it yet, it was just an observation related to the other comments.
-
Rams over 4 yearsJava 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 toprivate static synthetic lambda$0(Ljava/lang/String;)
.