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;
What I know, no sub classes or anonymous classes are being created in both of the above cases. This is creating couple of confusions to me:
1 : Since the variable consumer
is not referring to subclass or Anonymous class of Consumer
, so isn't this violating the above mentioned concept which is, variable 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