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);
}
}
A functional interface is an interface that contains only one abstract method. They can have only one functionality to exhibit. From Java 8 onwards, lambda expressions can be used to represent the instance of a functional interface. A functional interface can have any number of default methods.
The Lambda expression is used to provide the implementation of an interface which has functional interface. It saves a lot of code. In case of lambda expression, we don't need to define the method again for providing the implementation. Here, we just write the implementation code.
Approach 6: Use Standard Functional Interfaces with Lambda Expressions. This is a very simple interface. It's a functional interface because it contains only one abstract method. This method takes one parameter and returns a boolean value.
Runnable is a functional interface, that's why we can use lambda expression to create it's instance. Since run() method takes no argument, our lambda expression also have no argument. Just like if-else blocks, we can avoid curly braces ({}) since we have a single statement in the method body.
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));
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);
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));
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.
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