Java Lambda Expressions, introduced in Java 8, provide a clear and concise way to represent one method interface using an expression.
They are particularly useful for functional interfaces, which are interfaces with a single abstract method.
Lambda expressions help simplify code by eliminating the need for anonymous classes, making code more readable and easier to maintain.
Syntax of Lambda Expressions
The syntax of a lambda expression is:
(parameters) -> expression (parameters) -> { statements; }
- (parameters): The input parameters for the method.
- ->: The arrow token separates parameters from the expression or block of code.
- expression or { statements; }: The body of the lambda expression, which can either be a single expression or a block of statements.
Key Points about Lambda Expressions
- Functional Interface Requirement: Lambda expressions can only be used with functional interfaces.
- Type Inference: The types of parameters can be inferred by the compiler.
- Conciseness: Lambdas simplify code by reducing boilerplate syntax.
Example 1: Basic Lambda Expression with Runnable
The Runnable interface is a functional interface with a single run() method, which is ideal for a lambda expression.
Example
public class LambdaRunnableExample { public static void main(String[] args) { // Using lambda expression for Runnable Runnable task = () -> System.out.println("Task is running"); // Start a thread with the lambda expression Thread thread = new Thread(task); thread.start(); } }
Explanation
- We create a Runnable using a lambda expression instead of an anonymous inner class.
- Runnable task = () -> System.out.println(“Task is running”); represents the run() method of Runnable.
Output
Task is running
Example 2: Lambda Expression with Parameters
This example uses a lambda expression with parameters in the Comparator interface to compare two strings by their lengths.
Example
import java.util.Arrays; import java.util.Comparator; public class LambdaComparatorExample { public static void main(String[] args) { String[] names = { "John", "Alice", "Bob", "Charlie" }; // Using lambda expression to sort by string length Arrays.sort(names, (s1, s2) -> Integer.compare(s1.length(), s2.length())); // Display sorted array System.out.println(Arrays.toString(names)); } }
Explanation
- (s1, s2) -> Integer.compare(s1.length(), s2.length()) is a lambda expression that implements the compare method of Comparator<String>.
- This lambda expression sorts strings by their lengths.
Output
[Bob, John, Alice, Charlie]
Example 3: Using Lambda with Custom Functional Interface
You can define your own functional interface to use with lambda expressions.
Example
@FunctionalInterface interface Greeting { void sayHello(String name); } public class LambdaCustomInterfaceExample { public static void main(String[] args) { // Lambda expression for Greeting interface Greeting greeting = name -> System.out.println("Hello, " + name + "!"); // Use the lambda expression greeting.sayHello("Alice"); greeting.sayHello("Bob"); } }
Explanation
- Greeting is a custom functional interface with a single method sayHello.
- name -> System.out.println(“Hello, ” + name + “!”) is a lambda that implements sayHello to greet a user by name.
Output
Hello, Alice! Hello, Bob!
Example 4: Lambda Expression with Block of Statements
If the lambda body has multiple statements, enclose them in curly braces { }.
Example
@FunctionalInterface interface MathOperation { int operate(int a, int b); } public class LambdaBlockExample { public static void main(String[] args) { // Lambda expression with multiple statements in the body MathOperation addition = (a, b) -> { System.out.println("Adding " + a + " and " + b); return a + b; }; int result = addition.operate(5, 3); System.out.println("Result: " + result); } }
Explanation
- (a, b) -> { System.out.println(“Adding ” + a + ” and ” + b); return a + b; } has a block of statements within the lambda.
- The lambda prints each operand before performing the addition.
Output
Adding 5 and 3 Result: 8
Example 5: Lambda Expressions with Predicate Functional Interface
The Predicate<T> interface is a generic functional interface that accepts a single argument and returns a boolean result. It’s commonly used in filter operations.
Example
import java.util.Arrays; import java.util.List; import java.util.function.Predicate; public class LambdaPredicateExample { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); // Predicate to check if a string starts with "A" Predicate<String> startsWithA = s -> s.startsWith("A"); // Filter and print names starting with "A" names.stream() .filter(startsWithA) .forEach(System.out::println); } }
Explanation
- Predicate<String> startsWithA = s -> s.startsWith(“A”) checks if a string starts with “A”.
- filter(startsWithA) filters the stream to include only names that start with “A”.
Output
Alice
Example 6: Using Lambda with Function Interface
The Function<T, R> interface is a functional interface that accepts one argument of type T and returns a result of type R.
Example
import java.util.function.Function; public class LambdaFunctionExample { public static void main(String[] args) { // Function to calculate the square of an integer Function<Integer, Integer> square = x -> x * x; // Use the function System.out.println("Square of 5: " + square.apply(5)); System.out.println("Square of 10: " + square.apply(10)); } }
Explanation
- Function<Integer, Integer> square = x -> x * x; is a lambda that calculates the square of a number.
- apply is called with the argument to calculate and print the square.
Output
Square of 5: 25 Square of 10: 100
Example 7: Method References with Lambda Expressions
Java provides method references as a shorthand notation for lambdas that call a single method. They are often used to refer to existing methods or constructors.
Example
import java.util.Arrays; import java.util.List; public class LambdaMethodReferenceExample { public static void main(String[] args) { List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); // Method reference instead of lambda expression names.forEach(System.out::println); } }
Explanation
- System.out::println is a method reference that refers to System.out.println(String).
- This replaces a lambda that would have called println on each name in the list.
Output
Alice Bob Charlie
Example 8: Lambda Expression with BiFunction
The BiFunction<T, U, R> functional interface takes two arguments of types T and U, and returns a result of type R.
Example
import java.util.function.BiFunction; public class LambdaBiFunctionExample { public static void main(String[] args) { // BiFunction to concatenate two strings with a space BiFunction<String, String, String> concatenate = (s1, s2) -> s1 + " " + s2; // Use the BiFunction String result = concatenate.apply("Hello", "World"); System.out.println("Concatenated String: " + result); } }
Explanation
- (s1, s2) -> s1 + ” ” + s2 is a lambda that concatenates two strings with a space in between.
- The apply method is called to concatenate “Hello” and “World”.
Output
Concatenated String: Hello World
Summary
Java lambda expressions simplify working with functional interfaces, making code more concise and readable.
Key points include:
- Syntax: (parameters) -> expression or (parameters) -> { statements }.
- Functional Interfaces: Lambdas work with interfaces that have a single abstract method.
- Common Functional Interfaces:
- Runnable, Comparator, Predicate, Function, BiFunction, etc.
- Method References: Provide a shorthand for lambdas that only call one method.
Lambda expressions are a core feature for functional programming in Java, making it easier to work with collections, streams, and functional interfaces.