Explain lambda expressions in Java.
Lambda expressions, introduced in Java 8, are a powerful feature that brings functional programming paradigms to Java. They provide a concise way to represent an instance of a functional interface, enabling more readable and maintainable code, especially when working with collections and the Stream API.
What are Lambda Expressions?
A lambda expression is essentially an anonymous function – a function without a name, but with parameters and a body. It's a way to treat functionality as a method argument, or code as data. Lambdas are primarily used to implement functional interfaces, which are interfaces with a single abstract method.
Syntax of a Lambda Expression
The basic syntax of a lambda expression consists of three parts: parameters, the arrow token (->), and the body. The general form is (parameters) -> { body }.
- Parameters: A comma-separated list of formal parameters enclosed in parentheses. If there are no parameters, an empty pair of parentheses
()is used. Type declaration for parameters is optional, as the compiler can often infer them. - Arrow Token (
->): Separates the parameters from the lambda body. - Body: Contains the expression or block of code that the lambda executes. If the body contains a single expression, curly braces
{}and thereturnkeyword are optional. For multiple statements, curly braces and explicitreturn(if applicable) are required.
Examples of Lambda Syntax
// No parameters, prints a message
() -> System.out.println("Hello Lambda!");
// One parameter, infers type, returns square
x -> x * x;
// Two parameters, explicit types, returns sum
(int a, int b) -> a + b;
// Multiple statements in body, explicit return
(String message) -> {
System.out.print("Received: ");
return message.toUpperCase();
};
Functional Interfaces
A functional interface is an interface that has exactly one abstract method. Lambda expressions are used to provide the implementation for this single abstract method. The @FunctionalInterface annotation (optional but recommended) is used to indicate that an interface is intended to be a functional interface and ensures it adheres to the single abstract method rule.
@FunctionalInterface
interface Calculator {
int operate(int a, int b);
}
public class LambdaFunctionalInterfaceDemo {
public static void main(String[] args) {
// Using lambda to implement Calculator interface for addition
Calculator adder = (a, b) -> a + b;
System.out.println("Sum: " + adder.operate(10, 5)); // Output: Sum: 15
// Using lambda for multiplication
Calculator multiplier = (a, b) -> a * b;
System.out.println("Product: " + multiplier.operate(10, 5)); // Output: Product: 50
}
}
Java provides several built-in functional interfaces in the java.util.function package, such as Predicate, Consumer, Function, and Supplier.
Benefits of Using Lambda Expressions
- Concise Code: Reduces boilerplate code, especially for anonymous inner classes.
- Readability: For simple operations, lambdas can make code easier to understand.
- Enables Functional Programming: Facilitates a more functional style of programming in Java.
- Easier Parallel Processing: Integrates seamlessly with the Java Stream API, making parallel operations simpler.
- Improved API Design: Allows designing APIs that accept behaviors as arguments.
Common Use Cases
- Collection Iteration: Using
forEach()method with collections. - Event Handling: Concise way to implement event listeners.
- Sorting: Defining custom comparison logic for sorting collections (e.g.,
Comparator). - Stream API: Most operations within the
StreamAPI (likefilter(),map(),reduce()) heavily rely on lambda expressions.
Lambda with Stream API Example
import java.util.Arrays;
import java.util.List;
public class StreamLambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Anna");
System.out.println("Original names:");
names.forEach(name -> System.out.println(name)); // Lambda for forEach
System.out.println("\nNames starting with 'A' and converted to uppercase:");
names.stream()
.filter(name -> name.startsWith("A")) // Lambda for filtering
.map(String::toUpperCase) // Method reference (shorthand for lambda)
.forEach(System.out::println); // Method reference for printing
}
}
Method References (Briefly)
Method references are a special type of lambda expression that are even more concise for lambdas that simply call an existing method. They reuse an existing method definition as a lambda body. For instance, System.out::println is a method reference equivalent to x -> System.out.println(x).