Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.

Java 8 Revealed: Lambdas, Default Methods and Bulk Data Operations

What if you no longer had to redeploy your Java code to see changes? The choice is yours. In just a few clicks you can Say Goodbye to Java Redeploys forever.

Decompiling lambdas

Let’s take a look under the covers now. It would be interesting to see, how the code is actually compiled when we use lambda expressions in our code.

Currently (as of Java 7 and before), if you wanted to emulate lambdas in Java, you have to define an anonymous inner class. This results in a dedicated class file after compilation. And if you have multiple such classes defined in the code they just get a number suffix in the name of the class file. What about lambdas?

Consider code like this:

public class Main {

  @FunctionalInterface
  interface Action {
    void run(String s);
  }

  public void action(Action action){
    action.run("Hello!");
  }

  public static void main(String[] args) {
    new Main().action((String s) -> System.out.print("*" + s + "*"));
  }

}

The compilation produces two class files: Main.class and Main$Action.class, and no numbered class which would usually appear for the anonymous class implementation. So there must be something in Main.class now that represents the implementation of the lambda expression that I’ve defined in main method.

$ javap -p Main

Warning: Binary file Main contains com.zt.Main
Compiled from "Main.java"
public class com.zt.Main {
  public com.zt.Main();
  public void action(com.zt.Main$Action);
  public static void main(java.lang.String[]);
  private static java.lang.Object lambda$0(java.lang.String);
}

Aha! There’s a generated method lambda$0 in the decompiled class! The -c -v switches will give us the real bytecode along with the constants pool definitions.

The main method reveals that invokedynamic is now issued to dispatch the call:

public static void main(java.lang.String[]);
   Code:
    0: new            #4       // class com/zt/Main
    3: dup
    4: invokespecial  #5       // Method "<init>":()V
    7: invokedynamic  #6,  0   // InvokeDynamic #0:lambda:()Lcom/zt/Main$Action;
   12: invokevirtual  #7       // Method action:(Lcom/zt/Main$Action;)V
   15: return

And in the constant pool it is possible to find the bootstrap method that links it all at runtime:

BootstrapMethods:
  0: #40 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:
    #41 invokeinterface com/zt/Main$Action.run:(Ljava/lang/String;)Ljava/lang/Object;
    #42 invokestatic com/zt/Main.lambda$0:(Ljava/lang/String;)Ljava/lang/Object;
    #43 (Ljava/lang/String;)Ljava/lang/Object;

You can see that MethodHandle API is used all around but we won’t dive in this right now. For now we can just confirm that the definition refers to the generated method lambda$0.

What if I define my own static method with the same name? lambda$0 is a valid identifier after all! So I defined my own lambda$0 method:

public static void lambda$0(String s){
  return null;
}

With this compilation failed, not allowing me to have this method in the code:

java: the symbol lambda$0(java.lang.String) conflicts with a
        compiler-synthesized symbol in com.zt.Main

It actually tells us that lambdas are captured before the other structures in the class during the compilation.

Summary

To draw a line here, for the Part 1 of this paper, we can definitely say that lambdas will have a great impact on Java very soon. The syntax is quite nice and once developers realize that these features provide value to their productivity, we will see a lot of code that leverages these features.


Download the PDF