26

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 therefore x -> 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}
}
ams
  • 60,316
  • 68
  • 200
  • 288
  • 1
    Try `consumer.getClass()` for hints, then read [this](http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html) and then the JLS. – Sotirios Delimanolis Feb 18 '14 at 15:41
  • 1
    @SotiriosDelimanolis `consumer.getClass()` outputs Example$$Lambda$1/1072591677 – ams Feb 18 '14 at 15:45
  • Use `javap -c YourClass.class` to check how the code gets compiled. – Luiggi Mendoza Feb 18 '14 at 15:48
  • I 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, but `0` is referencing something in the constant pool. – Sotirios Delimanolis Feb 18 '14 at 16:43

1 Answers1

15

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:

Community
  • 1
  • 1
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Note that in the Java 8 release implementation, Lambdas do not compile into separate class files. – Victor Grazi Oct 01 '14 at 18:39
  • @victor Right, these are all runtime generated classes. – Sotirios Delimanolis Oct 01 '14 at 18:51
  • Now they're not even classes. If you call getClass() you'll just get an exception. – Hakanai Oct 30 '14 at 00:46
  • @Trejkaz That doesn't seem right. Can you post an example? – Sotirios Delimanolis Oct 30 '14 at 00:49
  • Our 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. – Hakanai Oct 30 '14 at 03:15
  • @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. – Sotirios Delimanolis Oct 30 '14 at 04:55
  • Yeah, I didn't really have a question about it yet, it was just an observation related to the other comments. – Hakanai Oct 30 '14 at 05:03
  • 2
    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;)`**. – Rams Jul 19 '19 at 08:09