- Learning Java Functional Programming
- Richard M.Reese
- 605字
- 2025-02-20 23:45:52
Lambda expressions usage
A lambda expression can be used in many different situations, including:
- Assigned to a variable
- Passed as a parameter
- Returned from a function or method
We will demonstrate how each of these is accomplished and then elaborate on the use of functional interfaces. As you may remember from Chapter 1, Getting Started with Functional Programming, a functional interface is an interface that has one and only one abstract method.
Consider the forEach
method supported by several classes and interfaces, including the List
interface. In the following example, a List
interface is created and the forEach
method is executed against it. The forEach
method expects an object that implements the Consumer
interface. This will display the three cartoon character names:
List<String> list = Arrays.asList("Huey", "Duey", "Luey"); list.forEach(/* Implementation of Consumer Interface*/);
More specifically, the forEach
method expects an object that implements the accept
method, the interface's single abstract method. This method's signature is as follows:
void accept(T t)
The interface also has a default method, andThen
, which is passed and returns an instance of the Consumer
interface. We will discuss this in Chapter 3, Function Composition and Fluent Interfaces.
We can use any of three different approaches for implementing the functionality of the accept
method:
- Use an instance of a class that implements the
Consumer
interface - Use an anonymous inner class
- Use a lambda expression
We will demonstrate each method so that it will be clear how each technique works and why lambda expressions will often result in a better solution. We will start with the declaration of a class that implements the Consumer
interface as shown next:
public class ConsumerImpl<T> implements Consumer<T> { @Override public void accept(T t) { System.out.println(t); } }
We can then use it as the argument of the forEach
method:
list.forEach(new ConsumerImpl<>());
Using an explicit class allows us to reuse the class or its objects whenever an instance is needed.
The second approach uses an anonymous inner function as shown here:
list.forEach(new Consumer<String>() { @Override public void accept(String t) { System.out.println(t); } });
This was a fairly common approach used prior to Java 8. It avoids having to explicitly declare and instantiate a class, which implements the Consumer
interface. However, it is not easily reused and has issues accessing variables outside of the inner class as we will illustrate in the Closure in Java section.
A simple statement that uses a lambda expression is shown next:
list.forEach(t->System.out.println(t));
The lambda expression accepts a single argument and returns void. This matches the signature of the Consumer
interface. Java 8 is able to automatically perform this matching process. This process is covered in more detail in the Java 8 type inference section.
This latter technique obviously uses less code, making it more succinct than the other solutions. If we desire to reuse this lambda expression elsewhere, we could have assigned it to a variable first and then used it in the forEach
method as shown here:
Consumer consumer = t->System.out.println(t); list.forEach(consumer);
Anywhere a functional interface is expected, we can use a lambda expression. Thus, the availability of a large number of functional interfaces will enable the frequent use of lambda expressions and programs that exhibit a functional style of programming.
While developers can define their own functional interfaces, which we will do shortly, Java 8 has added a large number of functional interfaces designed to support common operations. Most of these are found in the java.util.function
package. We will use several of these throughout the book and will elaborate on their purpose, definition, and use as we encounter them. In the Functional interfaces revisited section, we will briefly introduce many others.