Java

Java 8: The First Taste of Lambdas

Project Lambda is the major theme in the upcoming Java 8 and probably the most awaited one among Java developers. There’s one interesting feature that is being added to Java along with lambdas – the defender methods. In this blog post my intention is to look under the covers – to see how lambdas are represented by the runtime and what bytecode instructions are involved during method dispatch.

Although Java 8 is yet to be released it is still possible to get a taste of what it will look like bydownloading the early access binaries for all the target platforms.

So you want to use lambdas, huh?

If you are familiar with other languages that include lambda expressions, such as Groovy or Ruby, you might be surprised at first that it is not as simple in Java. In Java, lambda expression is “SAM type”, which is an interface with a single abstract method (yes, interfaces can include non-abstract methods now – the defender methods).

So for instance the well known Runnable interface is perfectly suitable for serving as a SAM type:

Runnable r = () -> System.out.println("hello lambda!");

Or the same could be applied to the Comparable interface:

Comparator<Integer> cmp = (x, y) -> (x < y) ? -1 : ((x > y) ? 1 : 0);

The same can be written as follows:

Comparator<Integer> cmp = (x, y) -> {
  return (x < y) ? -1 : ((x > y) ? 1 : 0);
};

So it seems like the one-liner lambda expressions have implicit return for the statement.

What if I want to write a method that can accept a lambda expression as a parameter? Well, you have to declare the parameter as a functional interface, and then you can pass the lambda in:

interface Action {
   void run(String param);
}

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

Once we have a method that takes a functional interface as a parameter we can invoke it as follows:

execute((String s) -> System.out.println(s));

Effectively, the same expression can be replaced with a method reference since it is just a single method call with the same parameter:

execute(System.out::println);

However, if there’s any transformations going on with the argument, we can’t use method references and have to type the full lambda expression out:

execute((String s) -> System.out.println("*" + s + "*"));

I think the syntax is rather nice and we now have quite an elegant solution for lambdas in the Java language despite the fact Java doesn’t have functional types per se.

Functional interfaces in JDK8

As we learned, the runtime representation of a lambda is a functional interface (or a “SAM type”), an interface that defines only one abstract method. And although JDK already includes a number of interfaces, like Runnable and Comparable, that match the criteria, it is clearly not enough for API evolution. It just wouldn’t be as logical if we started using Runnables all around the code.

There’s a new package in JDK8, java.util.function, that includes a number of functional interfaces that are intended to be used by the new API. We won’t list all of them here – just do yourself a favour and study the package yourself 🙂

It seems that the library is evolving quite actively as some interfaces come and go. For instance it used to provide java.util.function.Block class which isn’t present in the latest build that I have at the time of writing this post:

anton$ java -version
openjdk version “1.8.0-ea”
OpenJDK Runtime Environment (build 1.8.0-ea-b75)
OpenJDK 64-Bit Server VM (build 25.0-b15, mixed mode)

As I discovered it is now replaced with Consumer interface and is used for all the new methods in the collections library. For instance, the Collection interface defines the forEach method as follows:

public default void forEach(Consumer<? super T> consumer) {
  for (T t : this) {
    consumer.accept(t);
  }
}

The interesting bit of the Consumer interface is that it actually defines one abstract method –accept(T t), and a defender method – Consumer<T> chain(Consumer<? extend T> consumer). It means that it is possible to chain the calls using this interface. I’m not quite sure how it will be used since I couldn’t find chain(..) method usages in the JDK library yet.

Also, I noticed that all the interfaces are marked with @FunctionalInterface runtime annotation. But aside its runtime presence the annotation is used by javac to verify if the interface is really a functional interface and there’s no more than one abstract method in it.

So if I try to compile code like this

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

The compiler will tell me:

java: Unexpected @FunctionalInterface annotation
Action is not a functional interface
multiple non-overriding abstract methods found in interface Action

While the following will compile just fine:

@FunctionalInterface
interface Action {
  void run(String param);
  default void stop(String param){}
}

Decompiling lambdas

I’m usually not as curious about the syntax and language features as I am about the runtime representation of those features. That is why one natural thing for me was to grab my favouritejavap utility and start reading the bytecode of the classes that include lambdas.

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 “”:()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.

I was curious, 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 Object 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

At the same time, if I removed the piece of code that defined lambda expression, the code compiled just fine. It actually tells us that lambdas are captured before the other structured in the class during the compilation, but that’s only my assumption.

Please note that in this example the lambda did not capture any of the variables and did not refer to any methods of the enclosing class. That is why the generated lambda$0 method isstatic. If the lambda refers to any of the enclosing context variables or methods, then a non-static method is generated. So don’t be mislead by the simple example – lambdas can capture enclosing context just fine!

Summary

We can definitely say that lambdas and the accompanied features (defender methods, collections library improvements) 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.

It was quite interesting for me to see what lambdas are compiled to and I was very happy when I saw the total utilization of the invokedynamic instruction in action without any anonymous inner classes involved at all.

From http://zeroturnaround.com/

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s