Bro*_*man 8 java lambda interface java-8
The code snippet shown below works. However, I'm not sure why it works. I'm not quite following the logic of how the lambda function is passing information to the interface.
Where is control being passed? How is the compiler making sense of each n in the loop and each message created?
This code compiles and gives the expected results. I'm just not sure how.
import java.util.ArrayList;
import java.util.List;
public class TesterClass {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Akira");
names.add("Jacky");
names.add("Sarah");
names.add("Wolf");
names.forEach((n) -> {
SayHello hello = (message) -> System.out.println("Hello " + message);
hello.speak(n);
});
}
interface SayHello {
void speak(String message);
}
}
Run Code Online (Sandbox Code Playgroud)
The SayHello is a Single Abstract Method interface which has a method that takes a string and returns void. This is analogous to a consumer. You are just providing an implementation of that method in form of a consumer which is similar to the following anonymous inner class implementation.
SayHello sh = new SayHello() {
@Override
public void speak(String message) {
System.out.println("Hello " + message);
}
};
names.forEach(n -> sh.speak(n));
Run Code Online (Sandbox Code Playgroud)
Fortunately, your interface has a single method such that the type system (more formally, the type resolution algorithm) can infer its type as SayHello. But if it were to have 2 or more methods, this would not be possible.
However, a much better approach is to declare the consumer before the for-loop and use it as shown below. Declaring the implementation for each iteration creates more objects than necessary and seems counter-intuitive to me. Here's the enhanced version using method references instead of lambda. The bounded method reference used here calls the relevant method on the hello instance declared above.
SayHello hello = message -> System.out.println("Hello " + message);
names.forEach(hello::speak);
Run Code Online (Sandbox Code Playgroud)
Update
Given that for stateless lambdas that does not capture anything from their lexical scope only once instance will ever be created, both of the approaches merely create one instance of the SayHello, and there's no any gain following the suggested approach. However this seems to be an implementation detail and I didn't know it until now. So a much better approach is just to pass in a consumer to your forEach as suggested in the comment below. Also note that all these approaches creates just one instance of your SayHello interface while the last one is more succinct. Here's how it looks.
names.forEach(message -> System.out.println("Hello " + message));
Run Code Online (Sandbox Code Playgroud)
This answer will give you more insight on that. Here's the relevant section from the JLS §15.27.4: Run-Time Evaluation of Lambda Expressions
These rules are meant to offer flexibility to implementations of the Java programming language, in that:
- A new object need not be allocated on every evaluation.
In fact, I initially thought every evaluation creates a new instance, which is wrong. @Holger thanks for pointing that out, good catch.