![]()
Lambda Expressions in Java
Introduced in Java 8, Lambda expressions are a key feature that enables functional programming capabilities in Java. They provide a concise way to express instances of single-method interfaces (functional interfaces) using an expression rather than implementing the interface with a verbose class.
Lambda expressions allow for more readable and maintainable code, particularly when working with functional-style operations like filtering, mapping, and reducing, especially in conjunction with the Streams API.
1. What is a Lambda Expression?
A Lambda expression is a function (or anonymous method) that can be used to create instances of functional interfaces. A functional interface is an interface that has only one abstract method.
Syntax of Lambda Expression:
(parameters) -> expression
- Parameters: These represent the input parameters to the lambda. If there are multiple parameters, they are enclosed in parentheses, similar to a method.
- Arrow token (
->): It separates the parameters and the body of the lambda expression. - Expression/Body: This is the logic that the lambda performs.
Example:
// Traditional approach with anonymous class
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello, World!");
}
};
// Using lambda expression
Runnable r2 = () -> System.out.println("Hello, World!");
In the example above:
- The traditional approach creates an anonymous class to implement the
Runnableinterface. - The lambda expression simplifies this by directly providing the implementation.
2. Lambda Expression Components
Lambda expressions consist of three main parts:
- Parameters: A lambda expression can take zero or more parameters.
- Arrow Token (
->): Separates the parameters and the body. - Body: This part defines what the lambda expression does.
Examples:
- No parameters:
() -> System.out.println("Hello!"); - One parameter:
x -> x * x; // Squaring a number - Multiple parameters:
(a, b) -> a + b; // Adding two numbers
3. Functional Interfaces
A functional interface is an interface with exactly one abstract method, and it may have multiple default or static methods. Lambdas can be used to implement functional interfaces.
Some common functional interfaces in Java are:
RunnableComparator<T>Callable<V>Function<T, R>Consumer<T>Supplier<T>Predicate<T>
The @FunctionalInterface annotation is optional but highly recommended to indicate that an interface is intended to be a functional interface.
Example:
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod(); // Single abstract method
default void defaultMethod() { // Default method
System.out.println("This is a default method");
}
}
In this example, MyFunctionalInterface is a functional interface with one abstract method and a default method.
4. Lambda Expression Examples
4.1. Example 1: Using Lambda with Runnable
public class LambdaRunnableExample {
public static void main(String[] args) {
// Using lambda expression for Runnable
Runnable r = () -> System.out.println("Running in a thread");
new Thread(r).start();
}
}
In this example, we use a lambda to implement the Runnable interface to print a message when the thread runs.
4.2. Example 2: Using Lambda with Comparator
import java.util.*;
public class LambdaComparatorExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Jane", "Adam", "Mike");
// Using lambda expression to sort the list in reverse order
Collections.sort(names, (s1, s2) -> s2.compareTo(s1));
System.out.println(names); // Output: [Mike, John, Jane, Adam]
}
}
In this example, we use a lambda expression to provide the comparator for sorting a list of strings in reverse order.
5. Using Lambda Expressions with Java Collections
Lambda expressions are often used with the Collections Framework and Streams API to simplify operations such as filtering, transforming, and iterating over elements.
5.1. Example: Using Lambda with forEach
import java.util.*;
public class LambdaForEachExample {
public static void main(String[] args) {
List<String> list = Arrays.asList("Java", "Python", "JavaScript");
// Using lambda expression with forEach to print each element
list.forEach(element -> System.out.println(element));
}
}
In this example, forEach() is a method that accepts a lambda expression and applies it to each element in the list.
5.2. Example: Using Lambda with filter() and map() in Streams
import java.util.*;
import java.util.stream.*;
public class LambdaStreamsExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
List<Integer> evenSquares = numbers.stream()
.filter(n -> n % 2 == 0) // Filter even numbers
.map(n -> n * n) // Map to squares
.collect(Collectors.toList());
System.out.println(evenSquares); // Output: [4, 16, 36, 64]
}
}
In this example:
filter()is used with a lambda expression to select even numbers.map()is used with a lambda expression to square each of the even numbers.
6. Benefits of Lambda Expressions
- Concise Code: Lambdas eliminate the need for boilerplate code like anonymous classes, making the code more concise.
- Readability: Lambda expressions help make the code more readable by focusing on the logic of the operation.
- Functional Programming: Lambda expressions enable functional programming paradigms, like passing behavior as arguments.
- Parallelism: Combined with the Streams API, lambdas enable easy parallel processing of data.
7. Syntax Variations in Lambda Expressions
Lambda expressions in Java can be written in a variety of ways, depending on the complexity of the operation.
7.1. No Parameters:
() -> System.out.println("Hello, World!");
7.2. Single Parameter:
x -> x * x // Single parameter for squaring the number
7.3. Multiple Parameters:
(a, b) -> a + b // Adding two numbers
7.4. Block Body (Multiple statements):
(a, b) -> {
System.out.println("Adding numbers");
return a + b;
}
7.5. Returning Values:
() -> { return 42; } // Return a value in a block body
Lambda expressions in Java are a powerful feature introduced in Java 8 that simplify the development process by providing a concise and readable way to implement functional interfaces. They are particularly useful in conjunction with the Streams API, enabling developers to write cleaner, more expressive code for data manipulation, transformation, and processing.
Key Benefits:
- Reduces boilerplate code.
- Improves code readability and maintainability.
- Promotes a functional programming style.
- Simplifies code when working with the Collections Framework and Streams API.
