I am new to Lambda Expressions in Java so please don't react too harsh if this question appears silly: What is the actual object that gets implemented if I assign it to a FunctionalInterface
? I.e., if I do List<String> lst = new ArrayList<>()
I implement an ArrayList
object. What is the object that gets created if I do Function<String,String> fct = s -> s
? And is that object immutable, i.e. does it make sense to have a public static final Function<String,String> REUSE_OFTEN = s -> String.valueOf(s)
?
I.e., if I do List lst = new ArrayList<>() I implement an ArrayList object.
You instantiate a ArrayList
rather.
What is the object that gets created if I do Function fct = s -> s? And is that object immutable, i.e. does it make sense to have a public static final Function REUSE_OFTEN = s -> String.valueOf(s)?
Lambdas are not the same thing than anonymous classes in terms of byte codeand also in terms of behavior/functioning. This last one is close enough but not exactly the same.
Here are two methods that do the same thing, one with lambda the other with anonymous class :
public class CompareBoth {
public void lambda() {
Function<String,String> fct = s -> s;
fct.apply("a");
}
public void anonymousClass() {
Function<String,String> fct = new Function<String, String>() {
@Override
public String apply(String t) {
return t;
}
};
fct.apply("a");
}
}
Here the disassembled code :
public class cli.CompareBoth {
public cli.CompareBoth();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public void lambda();
Code:
0: invokedynamic #2, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;
5: astore_1
6: aload_1
7: ldc #3 // String a
9: invokeinterface #4, 2 // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
14: pop
15: return
public void anonymousClass();
Code:
0: new #5 // class cli/CompareBoth$1
3: dup
4: aload_0
5: invokespecial #6 // Method cli/CompareBoth$1."<init>":(Lcli/CompareBoth;)V
8: astore_1
9: aload_1
10: ldc #3 // String a
12: invokeinterface #4, 2 // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
17: pop
18: return
}
We can see that the JVM treat them differently.
What is the actual object that gets implemented ?
As a lambda is created this JVM instruction is performed :
0: invokedynamic #2, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;
InvokeDynamic
that was introduced in Java 7 is specified as a bytecode instruction that facilitates the implementation of dynamic languages (for the JVM) through dynamic method invocation.
You can read this javaworld page to have more information.
Now, does it really matter as you develop ?
In most of cases, it doesn't.
Functional interfaces are functionnaly close to anonymous interfaces with multiple slight differences.
Conceptually, I think that it is acceptable to consider them in this way.
About functional interface immutability :
Functional interfaces may have a state but are designed to be immutable.
So you can reuse them if it makes sense.
But InvokeDynamic
may be optimized by JVM.
So you should not reuse them in your code just to spare some objects.
Instead of, extract the functional interface in a constant or a variable because it makes things more clear or it avoids duplication.