I was creating the implementation of a functional interface, below is my code:
Consumer<Integer> consumer = new Consumer<Integer>() {
@Override
public void accept(Integer t) {
System.out.println(t);
}
};
As per the Java Documentation (javadoc)
A variable of a class type T can hold a null reference or a reference to an instance of class T or of any class that is a subclass of T.
In the code above, the anonymous object is created, which is a subclass of Consumer
, and can be referred to by reference variable consumer
, which is fine.
But I saw Consumer
is a FunctionalInterface
, so I can also do something like this in Java 8:
Using Lambda
Consumer<Integer> consumer = t -> System.out.println(t);
OR Using Method Reference
Consumer<Integer> consumer = System.out::println;
From what I know, no sub classes or anonymous classes are being created in both of the above cases. This result leaves me with two confusions:
1 : In the result of the lambda test, the variable consumer
is not referring to subclass or Anonymous class of Consumer
, so isn't this violating the above mentioned concept variable which can only refer child/self or null
?
2 : How is memory assigned to lambdas, and how does the JVM handle such at run time?
First of all Jean-Baptiste has shown you why the assignment works in the first place.
Now the part that I think that you are missing is the fact that the generated class of Consumer
in case of Consumer<Integer> consumer = t -> System.out.println(t);
is only visible at runtime due to invokedynamic
.
Run your class with a flag :
java -Djdk.internal.lambda.dumpProxyClasses=/Your/Path
And you will notice that there is a generated class (inside a path of folders from your package name of the class) that contains a .class
file sort of like this SOQuestion$$Lambda$1.class
.
If you decompile that you will see that it's actually a class that implements Consumer
:
final class org.eugene.so.SOQuestion$$Lambda$1
implements java.util.function.Consumer {
public void accept(java.lang.Object);
}
If you look at the generated byte code where your lambda is defined, you will see that the lambda expression is actually de-sugared to a static method (for non-captureting lambdas, it could also be non-static if it is a capturing lambda). The code for it:
private static void lambda$main$0(java.lang.Integer);
Code:
0: getstatic #3 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
7: return
As far as how the VM
manages memory for it, for an non-capturing lambda you will get a singleton, for a capturing-lambda you will get a new instance of each call. The absolute must read is here
You need to refer to SE8 15.27:
15.27.3. Type of a Lambda Expression
A lambda expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
Runtime handling use tricky things with invokedynamic
. Let examine some code:
import java.util.function.*;
class R implements Runnable {
public void run() { System.out.println("there"); }
}
public class L {
public static void execute(Runnable r) {
System.out.println(r.getClass());
r.run();
}
public static void main(String []a) {
execute(new R()); // subclass
execute(new Runnable() { // anonymous subclass
public void run() { System.out.println("elsewhere"); }
});
execute(() -> System.out.println("here")); // lambda
}
}
Execution gives:
> java L
class R
there
class L$1
elsewhere
class L$$Lambda$1/791452441
here
For the first two there is no surprise, the class of the object received by method execute
is (in the given order) R
(the subtype of Runnable
), L$1
(the anonymous subtype of Runnable
, and L$$Lambda$1/791452441
(the subtype of Runnable
constructed at run time from the lambda). Note that in the case of lambda there is no .class
file, the type is constructed at runtime by special construct. Let's examine the bytecode:
> javap -c -v L
Classfile /private/tmp/L.class
Last modified 1 août 2017; size 1234 bytes
MD5 checksum 9680a2bc143d25344979bae00fff3db7
Compiled from "L.java"
public class L
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #15.#28 // java/lang/Object."<init>":()V
#2 = Fieldref #29.#30 // java/lang/System.out:Ljava/io/PrintStream;
#3 = Methodref #15.#31 // java/lang/Object.getClass:()Ljava/lang/Class;
#4 = Methodref #32.#33 // java/io/PrintStream.println:(Ljava/lang/Object;)V
#5 = InterfaceMethodref #34.#35 // java/lang/Runnable.run:()V
#6 = Class #36 // R
#7 = Methodref #6.#28 // R."<init>":()V
#8 = Methodref #14.#37 // L.execute:(Ljava/lang/Runnable;)V
#9 = Class #38 // L$1
#10 = Methodref #9.#28 // L$1."<init>":()V
#11 = InvokeDynamic #0:#43 // #0:run:()Ljava/lang/Runnable;
#12 = String #44 // here
#13 = Methodref #32.#45 // java/io/PrintStream.println:(Ljava/lang/String;)V
#14 = Class #46 // L
#15 = Class #47 // java/lang/Object
#16 = Utf8 InnerClasses
#17 = Utf8 <init>
#18 = Utf8 ()V
#19 = Utf8 Code
#20 = Utf8 LineNumberTable
#21 = Utf8 execute
#22 = Utf8 (Ljava/lang/Runnable;)V
#23 = Utf8 main
#24 = Utf8 ([Ljava/lang/String;)V
#25 = Utf8 lambda$main$0
#26 = Utf8 SourceFile
#27 = Utf8 L.java
#28 = NameAndType #17:#18 // "<init>":()V
#29 = Class #48 // java/lang/System
#30 = NameAndType #49:#50 // out:Ljava/io/PrintStream;
#31 = NameAndType #51:#52 // getClass:()Ljava/lang/Class;
#32 = Class #53 // java/io/PrintStream
#33 = NameAndType #54:#55 // println:(Ljava/lang/Object;)V
#34 = Class #56 // java/lang/Runnable
#35 = NameAndType #57:#18 // run:()V
#36 = Utf8 R
#37 = NameAndType #21:#22 // execute:(Ljava/lang/Runnable;)V
#38 = Utf8 L$1
#39 = Utf8 BootstrapMethods
#40 = MethodHandle #6:#58 // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#41 = MethodType #18 // ()V
#42 = MethodHandle #6:#59 // invokestatic L.lambda$main$0:()V
#43 = NameAndType #57:#60 // run:()Ljava/lang/Runnable;
#44 = Utf8 here
#45 = NameAndType #54:#61 // println:(Ljava/lang/String;)V
#46 = Utf8 L
#47 = Utf8 java/lang/Object
#48 = Utf8 java/lang/System
#49 = Utf8 out
#50 = Utf8 Ljava/io/PrintStream;
#51 = Utf8 getClass
#52 = Utf8 ()Ljava/lang/Class;
#53 = Utf8 java/io/PrintStream
#54 = Utf8 println
#55 = Utf8 (Ljava/lang/Object;)V
#56 = Utf8 java/lang/Runnable
#57 = Utf8 run
#58 = Methodref #62.#63 // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#59 = Methodref #14.#64 // L.lambda$main$0:()V
#60 = Utf8 ()Ljava/lang/Runnable;
#61 = Utf8 (Ljava/lang/String;)V
#62 = Class #65 // java/lang/invoke/LambdaMetafactory
#63 = NameAndType #66:#69 // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#64 = NameAndType #25:#18 // lambda$main$0:()V
#65 = Utf8 java/lang/invoke/LambdaMetafactory
#66 = Utf8 metafactory
#67 = Class #71 // java/lang/invoke/MethodHandles$Lookup
#68 = Utf8 Lookup
#69 = Utf8 (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
#70 = Class #72 // java/lang/invoke/MethodHandles
#71 = Utf8 java/lang/invoke/MethodHandles$Lookup
#72 = Utf8 java/lang/invoke/MethodHandles
{
public L();
descriptor: ()V
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 9: 0
public static void execute(java.lang.Runnable);
descriptor: (Ljava/lang/Runnable;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokevirtual #3 // Method java/lang/Object.getClass:()Ljava/lang/Class;
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
10: aload_0
11: invokeinterface #5, 1 // InterfaceMethod java/lang/Runnable.run:()V
16: return
LineNumberTable:
line 11: 0
line 12: 10
line 13: 16
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: new #6 // class R
3: dup
4: invokespecial #7 // Method R."<init>":()V
7: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
10: new #9 // class L$1
13: dup
14: invokespecial #10 // Method L$1."<init>":()V
17: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
20: invokedynamic #11, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
25: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
28: return
LineNumberTable:
line 15: 0
line 16: 10
line 19: 20
line 20: 28
}
SourceFile: "L.java"
InnerClasses:
static #9; //class L$1
public static final #68= #67 of #70; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
BootstrapMethods:
0: #40 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#41 ()V
#42 invokestatic L.lambda$main$0:()V
#41 ()V
The first interesting part is the code of main
:
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: new #6 // class R
3: dup
4: invokespecial #7 // Method R."<init>":()V
7: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
10: new #9 // class L$1
13: dup
14: invokespecial #10 // Method L$1."<init>":()V
17: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
20: invokedynamic #11, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable;
25: invokestatic #8 // Method execute:(Ljava/lang/Runnable;)V
28: return
As you can observe there is no difference in between the explicit implementation of the interface or the anonymous one. The last only involves class naming trickery (L$1
), but both are used the same way, through invokestatic
.
Interesting case is the third (the lambda one) which involves invokedynamic
and then invokestatic
. Note that the invokestatic
calls the same method as in the two previous calls (the method run
).
Roughly, the first time the invokedynamic
is called a bootstrap method is called to construct a CallSite
(see CallSite in Java API) which will then be used further to execute the code of the lambda. See the bootstrap call here:
BootstrapMethods:
0: #40 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#41 ()V
#42 invokestatic L.lambda$main$0:()V
#41 ()V
and the code referred by the call site:
#41 = MethodType #18 // ()V
#42 = MethodHandle #6:#59 // invokestatic L.lambda$main$0:()V
#43 = NameAndType #57:#60 // run:()Ljava/lang/Runnable;
#44 = Utf8 here
#45 = NameAndType #54:#61 // println:(Ljava/lang/String;)V
#46 = Utf8 L
#47 = Utf8 java/lang/Object
#48 = Utf8 java/lang/System
#49 = Utf8 out
#50 = Utf8 Ljava/io/PrintStream;
#51 = Utf8 getClass
#52 = Utf8 ()Ljava/lang/Class;
#53 = Utf8 java/io/PrintStream
#54 = Utf8 println
#55 = Utf8 (Ljava/lang/Object;)V
#56 = Utf8 java/lang/Runnable
#57 = Utf8 run
Q: If here consumer is not referring to subclass or Anonymous class of Consumer ....?
In fact, the subclass is introduced in Linkage phase by invokedynamc
instruction.
Linkage may involve dynamically loading a new class that implements the target interface. The
CallSite
can be considered a "factory" for function objects and so these linkage methods are referred to as "metafactories".
Q: How memory assign to lamdas and how does JVM handle such at run time?
It proceeds in order through three phases:
For the more details, you can see LambdaMetafactory as further.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With