What is the Lambda expression in Java?
Lambda expressions in Java provide a concise way to represent anonymous functions or code blocks. They are mainly used to implement functional interfaces, which are interfaces that have only one abstract method.
Lambda expressions improve readability of the code, particularly when working with collections and functional programming.
Here’s the syntax of a lambda expression:
(parameters) -> { body }
- parameters: The input parameters for the lambda expression. If there are no parameters, you can use empty parentheses (). If there’s only one parameter, you can omit the parentheses. For multiple parameters, separate them with commas.
- body: The implementation of the lambda expression. It can be a single expression or a block of code. If it’s a single expression, you can omit the braces {}. If it’s a block of code, you need to use braces {} and may need to use the return keyword for the return value.
Let’s see some examples of using lambda expressions in Java:
Example: Simple Lambda Expression A lambda expression with no parameters and a single expression to print “Hello, lambda!”
public class RunnableExample { public static void main(String[] args) { // Traditional anonymous inner class implementation Runnable runnable1 = new Runnable() { @Override public void run() { System.out.println("Hello from traditional anonymous inner class!"); } }; // Lambda expression implementation Runnable runnable2 = () -> System.out.println("Hello from lambda expression!"); // Execute the run method of each runnable runnable1.run(); runnable2.run(); } }
Hello from traditional anonymous inner class!
Hello from lambda expression!
Explanation:
- public class RunnableExample: This is the declaration of the RunnableExample
- public static void main(String[] args): This is the main method of the class, where the program execution begins.
- Runnable runnable1 = new Runnable() { … }: This creates an instance of the Runnable interface using an anonymous inner class. The inner class overrides the run method of the Runnable interface and provides its implementation, which prints “Hello from traditional anonymous inner class!”.
- Runnable runnable2 = () -> System.out.println(“Hello from lambda expression!”);: This creates an instance of the Runnable interface using a lambda expression. The lambda expression () -> System.out.println(“Hello from lambda expression!”) represents an implementation of the run method, which also prints “Hello from lambda expression!”.
- run();: This calls the run method of the runnable1 instance, which triggers the execution of the overridden run method in the traditional anonymous inner class. As a result, “Hello from traditional anonymous inner class!” is printed.
- run();: This calls the run method of the runnable2 instance, which triggers the execution of the lambda expression. As a result, “Hello from lambda expression!” is printed.
Overall, the code demonstrates two different ways to implement the run method of the Runnable interface:
- The first implementation uses a traditional anonymous inner class.
- The second implementation uses a lambda expression.
Both implementations achieve the same goal: printing messages to the console. However, the lambda expression provides a more concise and expressive way to achieve this, especially for simple tasks like this one.
Example: Lambda Expression with Parameters:
A lambda expression with two parameters to calculate the sum of two numbers.
public class LambdaExample2 { interface Calculator { int add(int a, int b); } public static void main(String[] args) { // With lambda expression Calculator calculator = (a, b) -> a + b; int result = calculator.add(5, 10); System.out.println("Sum: " + result); // This will print "Sum: 15" } }
Output:
Sum: 15
Explanation:
- interface Calculator: This code defines a functional interface named Calculator. A functional interface is an interface that has only one abstract method. In this case, the Calculator interface defines a single method add that takes two integer parameters and returns an integer result.
- Calculator calculator = (a, b) -> a + b;: Here, a lambda expression is used to create an instance of the Calculator The lambda expression (a, b) -> a + b defines an implementation for the add method. The parameters a and b represent the input parameters of the add method, and the expression a + b calculates the sum of these two numbers. The lambda expression creates an implementation of the add method that adds two integers.
- int result = calculator.add(5, 10);: The calculator instance, which is an instance of the Calculator interface created using the lambda expression, is used to call the add The values 5 and 10 are passed as arguments to the add method. The result of the addition, 15, is stored in the result variable.
- out.println(“Sum: ” + result);: Finally, the calculated sum is printed to the console using the System.out.println statement.
Overall, this code demonstrates the use of a lambda expression to create an instance of a functional interface with a single method. The lambda expression provides a concise way to define the implementation of the add method. In this case, the lambda expression calculates the sum of two integer parameters.
Example: Lambda Expression with Multiple Statements:
A lambda expression with a block of code to find the maximum value in a list.
import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; public class LambdaExample3 { public static void main(String[] args) { List<Integer> numbers = Arrays.asList(1, 5, 3, 8, 2); // With lambda expression int max = Collections.max(numbers, (a, b) -> { System.out.println("Comparing " + a + " and " + b); return Integer.compare(a, b); }); System.out.println("Max value: " + max); // This will print "Max value: 8" } }
Output:
Comparing 1 and 5
Comparing 5 and 3
Comparing 5 and 8
Comparing 8 and 2
Max value: 8
Explanation:
- import java.util.Arrays;: Importing the Arrays class to work with arrays.
- import java.util.Collections;: Importing the Collections class to work with collection utilities, including finding the maximum value.
- import java.util.Comparator;: Importing the Comparator interface to potentially provide a custom comparison logic.
- import java.util.List;: Importing the List interface to work with lists.
- public class LambdaExample3 {: This declares the class named LambdaExample3.
- public static void main(String[] args) {: This is the entry point of the program.
- List<Integer> numbers = Arrays.asList(1, 5, 3, 8, 2);: This creates a list named numbers containing integers using the asList method.
- int max = Collections.max(numbers, (a, b) -> { … });: This uses the max method to find the maximum value in the numbers list. It takes a second argument, which is a lambda expression that defines a custom comparison logic. The lambda expression compares two elements a and b using Integer.compare(a, b).
- out.println(“Max value: ” + max);: This prints the maximum value obtained from the Collections.max method.
The key part of this example is the lambda expression (a, b) -> { … } passed as an argument to Collections.max. The lambda expression compares pairs of elements, and the comparisons are printed to the console due to the System.out.println statement within the lambda expression.
This code demonstrates how to use a lambda expression to provide custom comparison logic when finding the maximum value in a list using the Collections.max method. The lambda expression allows you to define the comparison directly inline, making the code concise and readable.
Why I will use Arrays.asList Method?
Arrays.asList is a method in Java that is used to convert an array into a fixed-size list. It is part of the java.util.Arrays class and provides a convenient way to work with arrays as lists, especially when dealing with APIs or methods that expect lists as input.
The syntax of Arrays.asList is as follows:
public static <T> List<T> asList(T... a)
Here, T is a generic type parameter that represents the type of elements in the array. The method takes a variable number of arguments (varargs) denoted by T… a, which means you can pass one or more elements of type T as arguments to the method.
The Arrays.asList method returns a List implementation that is backed by the original array. This implies that any modifications to the list will also affect the original array and vice versa. The returned list is fixed-size, which means you cannot add or remove elements from it. However, you can modify the existing elements.
Why I will use arrow signs like -> in lambda expressions?
The arrow sign (->) in lambda expressions serves as a separator between the parameters and the body of the lambda expression. It is a key part of the syntax used to define lambda functions in Java. The arrow operator visually represents the flow of data from the input parameters to the execution of the lambda expression’s body.
- parameters: The input parameters for the lambda expression. If there are no parameters, you can use empty parentheses (). If there’s only one parameter, you can omit the parentheses. For multiple parameters, separate them with commas.
- ->: The arrow operator separates the parameters from the body of the lambda expression. It indicates that the parameters are used to compute the result of the lambda expression.
- body: The implementation of the lambda expression. It can be a single expression or a block of code. If it’s a single expression, you can omit the braces {}. If it’s a block of code, you need to use braces {} and may need to use the return keyword for the return value.
The arrow operator is a visual cue that helps distinguish between the parameters and the body of the lambda expression, making the code more readable and expressive. It also serves as a clear indicator that you are defining a lambda function rather than a regular method or anonymous inner class.
For example, consider the following lambda expression to calculate the square of a number:
// Without lambda expression Function<Integer, Integer> square1 = new Function<Integer, Integer>() { @Override public Integer apply(Integer x) { return x * x; } }; // With lambda expression Function<Integer, Integer> square2 = x -> x * x;
In the second example, the arrow operator separates the parameter x from the body x * x, indicating that the lambda expression takes x as input and returns x * x as the result.
Using the arrow sign (->) in lambda expressions simplifies the syntax and makes it easier to represent functional-style operations in Java. It is an essential part of lambda expressions and contributes to the conciseness and readability of the code.
here’s an example of using lambda expressions with Java looping constructs:
import java.util.ArrayList; import java.util.List; public class LoopingWithLambdaExample { public static void main(String[] args) { List<String> fruits = new ArrayList<>(); fruits.add("Apple"); fruits.add("Banana"); fruits.add("Orange"); fruits.add("Mango"); // Looping using forEach and lambda expression System.out.println("Using forEach:"); fruits.forEach(fruit -> System.out.println(fruit)); // Looping using method reference System.out.println("\nUsing method reference:"); fruits.forEach(System.out::println); } }
Output:
Using forEach:
Apple
Banana
Orange
Mango
Using method reference:
Apple
Banana
Orange
Mango
Explanation:
- import java.util.ArrayList; and import java.util.List;: Importing the necessary classes for working with lists.
- public class LoopingWithLambdaExample {: This declares the class named LoopingWithLambdaExample.
- public static void main(String[] args) {: This is the entry point of the program.
- List<String> fruits = new ArrayList<>();: This creates an ArrayList named fruits to store strings.
- Adding fruits to the list using the add method:
fruits.add("Apple"); fruits.add("Banana"); fruits.add("Orange"); fruits.add("Mango");
6. Looping using forEach and Lambda Expression:
System.out.println("Using forEach:"); fruits.forEach(fruit -> System.out.println(fruit));
This uses the forEach method on the fruits list to loop through each element. The lambda expression fruit -> System.out.println(fruit) is used as an argument to forEach, and it prints each fruit to the console.
7. Looping using Method Reference:
System.out.println("\nUsing method reference:"); fruits.forEach(System.out::println);
- This also uses the forEach method to loop through each element and print it to the console. Here, a method reference out::println is used instead of a lambda expression. The System.out::println method reference refers to the println method of the System.out object, which is used to print the elements.
In both cases, the forEach method simplifies the process of looping through the list and performing an action on each element. Whether using a lambda expression or a method reference, the result is the same: printing each fruit in the list to the console. Method references provide a concise way to reference methods that can be invoked on each element of the collection.