In this last post we’ll look at the functional interface formal definition and the way lambda expressions are implemented.
DISCLAIMER: all the details exposed in this post were observed related to the JDK 8 demo version as of July 10th 2012. Since the JDK is still in beta, some assertions may not hold in future
Please note that all the example code in this post can be found on my GitHub repository https://github.com/doanduyhai/Java8_Lambda_In_Details
I Functional interface definition
It seems weird to give a definition of a functional interface (aka SAM interface) in the last post but the truth is that I discover the subtle semantic of functional interface only recently.
Usually people naively think that a functional interface must contain only one single abstract method. The real definition is less restrictive indeed.
An interface is considered a functional interface if it contains one and only one abstract method with no default implementation
No assertion is made about the possibility for this interface to declare static fields or many defenders methods. It means that a functional interface can
- have one abstract method and many defenders methods and static fields
- have one abstract method and inherit defenders from parent interfaces and static fields
The implication of these subtle details is huge indeed. We can have lambda expressions with default behaviors embedded (via defender methods)!
The code example in the next chapter will prove it.
II Lambda expression implementation
Did you ever wonder how the JDK 8 implements an lambda expression ? The following article gives us some insights.
Basically, the compiler will create anonymous inner classes to emulate the functioning of lambda expressions. Two possible scenarios there:
- the lambda expression is stateless and does not capture any variable: an anonymous inner class is created with a no argument constructor
- the lambda expression does capture variables (this or local variable): they are injected through anonymous inner class constructor as parameters
Let consider the following sample:
public interface SAMWithDefender { String staticvar = "static var in SAM"; void test(); void whoAmI() default { System.out.println("I am "+this); } } public class LambdaInstance { public static SAMWithDefender createLambda() { return () -> {System.out.println("");}; } public static SAMWithDefender createStatefullLambda(String input) { return () -> {System.out.println("input = "+input);}; } public static void main(String[] args) { SAMWithDefender samInstance1 = LambdaInstance.createLambda(); SAMWithDefender samInstance2 = LambdaInstance.createStatefullLambda("statefull"); System.out.println("\n"); samInstance1.whoAmI(); samInstance2.whoAmI(); System.out.println(""); } }
We define an interface with a static field and 2 methods. One of them is provided a default implementation, the other is let abstract (line 5). This interface meets the requirement to be a functional interface (one single abstract method) as mentioned in the previous chapter.
In the main class, we define 2 lambda expressions, one stateless (line 28) and one capturing local variable (line 29).
I compile the class and use the “javap -verbose” command to examine the generated bytecode.
Definition of fr.doan.lambda.instance.LambdaInstance class:
{ public fr.doan.lambda.instance.LambdaInstance(); 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 5: 0 public static fr.doan.lambda.sam.instance.SAMWithDefender createLambda(); flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=0, args_size=0 0: new #2 // class fr/doan/lambda/instance/LambdaInstance$1 3: dup 4: invokespecial #3 // Method fr/doan/lambda/instance/LambdaInstance$1."<init>":()V 7: areturn LineNumberTable: line 10: 0 public static fr.doan.lambda.sam.instance.SAMWithDefender createStatefullLambda(java.lang.String); flags: ACC_PUBLIC, ACC_STATIC Code: stack=3, locals=1, args_size=1 0: new #4 // class fr/doan/lambda/instance/LambdaInstance$2 3: dup 4: aload_0 5: invokespecial #5 // Method fr/doan/lambda/instance/LambdaInstance$2."<init>":(Ljava/lang/String;)V 8: areturn LineNumberTable: line 15: 0 }
At lines 16 & 18, the static method createLambda() is instanciating a new anonymous inner class “fr/doan/lambda/instance/LambdaInstance$1” with no argument constructor.
At lines 27 & 30, the static method createStatefullLambda() is instanciating a new anonymous inner class “fr/doan/lambda/instance/LambdaInstance$2” with the captured method parameter as constructor argument.
Let’s decompile the anonymous inner class “fr/doan/lambda/instance/LambdaInstance$2”
{ class fr.doan.lambda.instance.LambdaInstance$2 implements fr.doan.lambda.sam.instance.SAMWithDefender SourceFile: "LambdaInstance.java" EnclosingMethod: #24.#25 // fr.doan.lambda.instance.LambdaInstance.createStatefullLambda InnerClasses: static #10; //class fr/doan/lambda/instance/LambdaInstance$2 java.lang.String cap$0; flags: ACC_SYNTHETIC fr.doan.lambda.instance.LambdaInstance$2(java.lang.String); flags: Code: stack=2, locals=2, args_size=2 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: aload_0 5: aload_1 6: putfield #2 // Field cap$0:Ljava/lang/String; 9: return LineNumberTable: line 15: 0 ... ... }
We clearly see at lines 7-8 that the compiler generated a synthetic field cap$0 (capture0) to capture the local variable and passed it to the constructor argument (line 10)
When executing the LambdaInstance class, the output shows:
I am fr.doan.lambda.instance.LambdaInstance$1@59dbd
I am fr.doan.lambda.instance.LambdaInstance$2@b6e768
That’s all folks!
The above scenario can be illustrated by a code example on GitHub at https://github.com/doanduyhai/Java8_Lambda_In_Details. Just execute the LambdaInstance.bat(LambdaInstance.sh) script
Hey, really a detailed and interesting set of articles.
please visit http://amitrp.blogspot.in/2012/08/at-first-sight-with-closures-in-java.html where i have put a detailed explanations of Java Closures with working examples.
Hmm is anyone else having problems with the pictures on this blog loading?
I’m trying to determine if its a problem on my end or if it’s the blog.
Any suggestions would be greatly appreciated.