I learnt that invokedynamic
instruction of bytecode calls a static
method representation of lambda.
Please let me know if that is incorrect.
if correct, then how is below code working?
String[] arr = new String[1]; Stream.of(arr).forEach((s) -> System.out.println(this));
Advertisement
Answer
It’s not correct to say that lambda expressions were always compiled to a static
method. It’s not specified, how they are compiled, which leaves room for two different strategies for a lambda expression that captures this
, like your s -> System.out.println(this)
.
use an instance method:
private void compiler$chosen$name(String s) { System.out.println(this); }
use a
static
method:private static void compiler$chosen$name(TypeOfThis var0, String s) { System.out.println(var0); }
Both methods work equally well when the invokedynamic
instruction points to a bootstrap method in the LambdaMetafactory
. In either case, the invokedynamic
instruction will have a signature consuming a TypeOfThis
instance and producing a Consumer<String>
. From the documentation of the LambdaMetafactory
, you can derive that it will treat the receiver of a non-static
target method like an implied the first argument, which makes the functional signature of both variants identical. All that matters, is that the argument to the consumer’s accept
method has to correspond to the last argument of the list.
I’ve encountered both strategies in practice, so this is indeed compiler dependent.
Note that these strategies also work on source code level, when using method references:
public class Example { BiConsumer<Example,String> variant1 = Example::instanceMethod; BiConsumer<Example,String> variant2 = Example::staticMethod; private void instanceMethod(String s) { System.out.println(this); } private static void staticMethod(Example instance, String s) { System.out.println(instance); } }
This demonstrates the equivalence of a method receiver and the the first argument to a static
method. However, when it comes to binding an argument, only Consumer<String> c = this::instanceMethod;
works with method references. The other binding features of the LambdaMetafactory
are only used by compiler generated code for lambda expressions.