Java - Lambda Expressions
1. What are Lambda Expressions?
A lambda expression is a short block of code that takes in parameters and returns a value. It can be used as a concise way to represent instances of functional interfaces (interfaces with a single abstract method).
Before Java 8, using anonymous classes was the standard way to pass behaviour to methods, but lambda expressions provide a more elegant and readable alternative.
Example:
// Traditional way using an anonymous class
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Hello, World!");
}
};
// Using a lambda expression
Runnable runnableLambda = () -> System.out.println("Hello, World!");
2. Why Use Lambda Expressions?
Benefits of Lambda Expressions:
Conciseness: Reduces boilerplate code, making your codebase cleaner.
Readability: Makes the code easier to understand, especially for simple operations.
Functional Programming: Enables a functional programming style in Java, leading to more declarative code.
Better Use of Collections API: Works seamlessly with Java Streams and functional-style operations like map, filter, and reduce.
3. Syntax of Lambda Expressions
The syntax of a lambda expression is simple and consists of three parts:
(parameterList) -> { body }
Examples of Different Lambda Syntaxes:
No Parameters:
() -> System.out.println("Hello, Lambda!");
Single Parameter (Parentheses Optional):
name -> System.out.println("Hello, " + name);
Multiple Parameters:
(a, b) -> a + b;
With a Block of Code:
(int x, int y) -> {
int sum = x + y;
return sum;
};
4. Functional Interfaces
A functional interface is an interface with exactly one abstract method. These interfaces are used as the types for lambda expressions. Java 8 provides several built-in functional interfaces in the java.util.function package.
Examples of Functional Interfaces:
Runnable: void run()
Callable<T>: T call()
Comparator<T>: int compare(T o1, T o2)
Predicate<T>: boolean test(T t)
Function<T, R>: R apply(T t)
Consumer<T>: void accept(T t)
Supplier<T>: T get()
Example:
@FunctionalInterface
interface MyFunctionalInterface {
void display();
}
// Using Lambda Expression
MyFunctionalInterface message = () -> System.out.println("Hello Functional Interface!");
message.display();
5. Examples of Lambda Expressions
5.1. Using Lambdas with Collections
Sorting a List:
List<String> names = Arrays.asList("John", "Alice", "Bob");
// Using Lambda Expression to sort
names.sort((s1, s2) -> s1.compareTo(s2));
System.out.println(names);
Filtering a List:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
// Using Lambda Expression to filter even numbers
numbers.stream()
.filter(n -> n % 2 == 0)
.forEach(System.out::println);
5.2. Using Lambdas with Streams
The Streams API leverages lambda expressions to provide a functional approach to processing sequences of elements.
Example: Map and Filter with Streams
List<String> names = Arrays.asList("John", "Jane", "Jack", "Doe");
// Using Streams and Lambdas to filter and map
names.stream()
.filter(name -> name.startsWith("J"))
.map(String::toUpperCase)
.forEach(System.out::println);
Output:
JOHN
JANE
JACK
6. Method References
Method references are a shorthand notation of lambdas to call methods. They provide a more readable and concise way to use lambdas.
Types of Method References:
Reference to a Static Method: ClassName::methodName
Reference to an Instance Method: object::methodName
Reference to a Constructor: ClassName::new
Example:
List<String> names = Arrays.asList("John", "Alice", "Bob");
// Using Method Reference
names.forEach(System.out::println);
7. Conclusion
Lambda expressions are a game-changer in Java, enabling a more functional programming style and reducing boilerplate code. They allow for cleaner, more concise, and readable code, especially when working with collections and streams.