21
NovLambda Expressions in Java: Explained in Easy Steps
Lambda Expression in Java
Lambda Expression in Java is a new and important feature of Java that was included in Java SE 8. It provides a clear and concise way to represent one method interface using an expression. It is very useful in the Java collection library. It helps to iterate, filter, and extract data from the collection. The Lambda expression is used to implement a functional interface.
In the Java tutorial, we will learn what is lambda expression in Java?, the syntax of lambda expressions,understand functional interfaces, types of lambda expressions in Java, lambdas vs method references, anonymous classes vs lambda expression, lambdas vs streams API, lambdas vs method references, and many more.
What is Lambda Expression in Java?
Lambda Expressions in Java are specific sections of code that function like standard methods. They take in a collection of parameters and provide a value as the result. In contrast to methods, a lambda expression does not always need a name. It saves a lot of code. In the case of the lambda expression, we don't need to define the method again to provide the implementation.
- Lambda expressions were introduced to provide a clear and concise way to represent one method interface using an expression.
- Allows for more readable and compact code by eliminating the need for anonymous classes.
- Works with interfaces that have a single abstract method (e.g., Runnable, Comparator).
- Often used with the Streams API for filtering, mapping, and iterating over collections.
- Makes code more concise and easier to understand, especially in operations involving collections.
- The type of parameters can be inferred, so you don’t always need to specify them explicitly.
Read More: |
What is Java? A Beginner Guide to Java |
Java Developer Salary Guide in India – For Freshers & Experienced |
Lambda Function in Python with Examples (Full Tutorial) |
Syntax of Lambda Expression in Java
(argument-list) -> {body}
1. Syntax for the Zero Parameter
() -> System.out.println("Zero parameter lambda");
2. Syntax for the One Parameter
(p) -> System.out.println("One parameter: " + p);
3. Syntax for the Multiple Parameter
(p1, p2) -> System.out.println("Multiple parameters: " + p1 + ", " + p2);
Expression in Java consists of three components:
- Argument list: It can be empty or non-empty as well.
- Arrow-token: It is used to link the arguments list and body of expression.
- Body: It contains expressions and statements for lambda expression.
What is Functional Interfaces?
Functional interface in Java is an interface with just one abstract method on it. The contract that the lambda expression or method reference will satisfy is defined by this one abstract method. In Java, lambda expressions and method references are used primarily with functional interfaces in mind.
Key points to be remembered for the Functional Interface:
- A functional interface must have only one abstract method. This method is called the functional method.
- Using the @FunctionalInterface annotation is a best practice to ensure the interface has only one abstract method, as it enforces this rule at compile-time.
- A functional interface can have any number of default or static methods as long as it has only one abstract method.
Example: Functional Interface
@FunctionalInterface
interface ScholarHat {
void learn(); // Single abstract method
}
Example: Using Functional Interface in Lambda Expression
@FunctionalInterface
interface ScholarHat {
void study(); // Single abstract method
}
public class LambdaExample {
public static void main(String[] args) {
// Using a lambda expression to implement the ScholarHat interface
ScholarHat learningSession = () -> System.out.println("Studying with ScholarHat!");
learningSession.study(); // Output: Studying with ScholarHat!
}
}
Output
Studying with ScholarHat!
Read More: |
Java Full Stack Developer Roadmap for Beginners (Updated 2024) |
Top 10 Reasons to Know Why Java is Important? |
Types of Lambda Expression in Java
1. Lambda Expression in Java with Zero Parameter
A lambda expression with no parameters directly provides an implementation for the functional interface's method.
Example
@FunctionalInterface
interface Greet {
// Single abstract method for the functional interface
void sayHello();
}
public class LambdaExample {
public static void main(String[] args) {
// Lambda expression implementing the sayHello method of the Greet interface
Greet greeting = () -> System.out.println("Hello, World!");
// Calling the sayHello method, which executes the lambda expression
greeting.sayHello();
}
}
Output
Hello, World!
2. Lambda Expression in Java with Single Parameter
A lambda expression with one parameter doesn't need parentheses around the parameter.
Example
@FunctionalInterface
interface Square {
// Single abstract method that takes an integer and returns its square
int calculate(int x);
}
public class LambdaExample {
public static void main(String[] args) {
// Lambda expression implementing the calculate method of the Square interface
Square square = x -> x * x;
// Calling the calculate method with the argument 5, which computes 5 * 5
System.out.println(square.calculate(5));
}
}
Output
25
3. Lambda Expression in Java with Multiple Parameters
A lambda expression with multiple parameters requires parentheses around the parameters.
Example
@FunctionalInterface
interface Add {
// Single abstract method that takes two integers and returns their sum
int sum(int a, int b);
}
public class LambdaExample {
public static void main(String[] args) {
// Lambda expression implementing the sum method of the Add interface
Add addition = (a, b) -> a + b;
// Calling the sum method with arguments 10 and 20, which computes 10 + 20
System.out.println(addition.sum(10, 20));
}
}
Output
30
4. Lambda Expression in Java with Block of Code
A lambda expression with a block of code requires curly braces and a return statement if there is a return value.
Example
@FunctionalInterface
interface Multiply {
// Single abstract method that takes two integers and returns their product
int multiply(int a, int b);
}
public class LambdaExample {
public static void main(String[] args) {
// Lambda expression implementing the multiply method of the Multiply interface
Multiply multiplication = (a, b) -> {
// Calculate the product of a and b
int result = a * b;
// Return the result
return result;
};
// Calling the multiply method with arguments 4 and 5, which computes 4 * 5
System.out.println(multiplication.multiply(4, 5));
}
}
Output
20
5. Lambda Expression in Java with Method Reference
While not a lambda expression per se, method references provide a shorthand way to write lambdas that call a specific method.
Example
@FunctionalInterface
interface Printer {
// Single abstract method that takes a String message and prints it
void print(String message);
}
public class LambdaExample {
public static void main(String[] args) {
// Method reference to System.out.println,
//implementing the print method of the Printer interface
Printer printer = System.out::println;
// Calling the print method with the message "Hello, Method Reference!"
printer.print("Hello, Method Reference!"); // Output: Hello, Method Reference!
}
}
Output
Hello, Method Reference!
Examples of Lambda Expressions in Java
Here are some of the most important examples that will help you to understand Lambda Expression in Java properly:
1. Without Using Lambda Expression
@FunctionalInterface
interface Greet {
void sayHello();
}
public class ScholarHat {
public static void main(String[] args) {
// Implementing the Greet interface using an anonymous class
Greet greeting = new Greet() {
@Override
public void sayHello() {
System.out.println("Hello from ScholarHat!");
}
};
// Calling the sayHello method
greeting.sayHello();
}
}
Output
Hello from ScholarHat!
2. By Using Lambda Expression
@FunctionalInterface
interface Greet {
// Single abstract method to be implemented
void sayHello();
}
public class ScholarHat {
public static void main(String[] args) {
// Implement the Greet interface using a lambda expression
Greet greeting = () -> System.out.println("Hello from ScholarHat!");
// Call the sayHello method of the Greet instance
greeting.sayHello();
}
}
Output
Hello from ScholarHat!
3. Iterating Collections Using the Foreach Loop
In Java, the forEach method of the Collection interface can be used to iterate over collections with the help of lambda expressions. This approach is both concise and readable. Here’s how you can use the forEach method along with lambda expressions to iterate over a collection.
import java.util.Arrays;
import java.util.List;
public class ScholarHat {
public static void main(String[] args) {
// Define a list of strings
List names = Arrays.asList("Aman", "Bhanu", "Charu", "Dev");
// Iterate over the list using forEach and a lambda expression
names.forEach(name -> System.out.println(name));
}
}
Output
Aman
Bhanu
Charu
Dev
Read More: |
for Loop in Java: Its Types and Examples |
while Loop in Java |
do...while Loop in Java - Flowchart & Syntax (With Examples) |
4. Java Lambda Expression With or Without Return Keyword
1. Lambda Expression Without 'return' Keyword
When a lambda expression has a single expression, you can omit the return keyword. This is known as an expression lambda.
@FunctionalInterface
interface Square {
int calculate(int x);
}
public class LambdaExample {
public static void main(String[] args) {
// Lambda expression without return keyword
Square square = x -> x * x;
// Calling the calculate method
System.out.println(square.calculate(5)); // Output: 25
}
}
Output
25
2. Lambda Expression With return Keyword
When a lambda expression contains multiple statements, you need to use curly braces {} and include the return keyword to specify the return value.
@FunctionalInterface
interface Square {
int calculate(int x);
}
public class LambdaExample {
public static void main(String[] args) {
// Lambda expression with return keyword
Square square = x -> {
int result = x * x;
return result;
};
// Calling the calculate method
System.out.println(square.calculate(5));
}
}
Output
25
5. Java Lambda Expression - Creating Thread
Lambda expressions in Java streamline thread creation by directly implementing the Runnable interface, making code more concise and readable. Before Java 8, thread creation required defining a new class or extending the Thread class.
public class LambdaThreadExample {
public static void main(String[] args) {
// Creating a thread using a lambda expression
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Running in thread: " + Thread.currentThread().getName() + ", Count: " + i);
try {
Thread.sleep(500); // Sleep for 500 milliseconds
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// Start the thread
thread.start();
// Main thread output
for (int i = 0; i < 5; i++) {
System.out.println("Running in main thread: " + Thread.currentThread().getName() + ", Count: " + i);
try {
Thread.sleep(500); // Sleep for 500 milliseconds
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Output
Running in thread: Thread-0, Count: 0
Running in main thread: main, Count: 0
Running in thread: Thread-0, Count: 1
Running in main thread: main, Count: 1
Running in thread: Thread-0, Count: 2
Running in main thread: main, Count: 2
Running in thread: Thread-0, Count: 3
Running in main thread: main, Count: 3
Running in thread: Thread-0, Count: 4
Running in main thread: main, Count: 4
6. Java Lambda Expression - Comparator
Lambda expressions simplify the implementation of the Comparator interface for sorting collections, making the code more concise and readable. Before Java 8, comparators were required to define new classes or anonymous classes.
import java.util.Arrays;
import java.util.List;
public class LambdaComparatorExample {
public static void main(String[] args) {
// Define a list of strings
List names = Arrays.asList("Aman", "Ankita", "Raghav", "Prince");
// Sort the list in natural order using a lambda expression
names.sort((s1, s2) -> s1.compareTo(s2));
// Print the sorted list
System.out.println("Sorted names: " + names);
}
}
Output
Sorted names: [Aman, Ankita, Prince, Raghav]
7. Java Lambda Expression - Filter Collection Data
Lambda expressions in Java can be used to filter data in collections, making it easier to process and manipulate data. The Stream API, introduced in Java 8, provides methods like filters that work seamlessly with lambda expressions for filtering data.
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class LambdaFilterExample {
public static void main(String[] args) {
// Define a list of strings
List names = Arrays.asList("Aman", "Ankita", "Radha", "Price", "Abhishek");
// Filter the list to include only names that start with "A"
List filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
// Print the filtered list
System.out.println("Filtered names: " + filteredNames);
}
}
Output
Filtered names: [Aman, Ankita, Abhishek]
8. Java Lambda Expression - Event Listener
Lambda expressions in Java can be used to simplify the implementation of event listeners, making your code more concise and readable. This is especially useful in GUI programming with Swing or JavaFX, where event handling is common.
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class LambdaEventListenerSwing {
public static void main(String[] args) {
// Create the JFrame
JFrame frame = new JFrame("Lambda Event Listener Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 200);
// Create a JPanel
JPanel panel = new JPanel();
// Create a JButton
JButton button = new JButton("Click Me!");
// Add ActionListener using lambda expression
button.addActionListener(event -> System.out.println("Button clicked!"));
// Add button to panel
panel.add(button);
// Add panel to frame
frame.add(panel);
// Make the frame visible
frame.setVisible(true);
}
}
Comparing Lambda Expressions with Other Functional Constructs
Lambda Expression in Java provides an easy approach to defining single-method interface (or functional interface) instances. However, there are a number of additional functional constructs in Java that have unique uses.
1. Anonymous Classes vs Lambda Expressions
This is the comparison of Java's anonymous classes and lambda expressions:
Factors | Anonymous Classes | Lambda Expressions |
Syntax | new Interface() { @Override method() { } } | parameters -> expression or parameters -> { statements } |
Use-cases | Implement interfaces or extend classes | Implement functional interfaces (single-method interfaces) |
Readability | More verbose and boilerplate code | More concise and readable |
Constructor Invocation | Can have constructors and instance initializers | It cannot have constructors; only method implementations |
Access to 'this' | Refers to the instance of the anonymous class | Refers to the enclosing class's instance |
Scope | Has its scope; can access enclosing class members | Inherits scope from the enclosing method/class |
Type | Creates a new class at runtime | Directly uses the target functional interface |
When to Use | Use it when you need to extend a class, implement multiple methods, or need more complex logic. | Use for simple implementations of functional interfaces where a single method needs to be implemented. |
Method Access | Can access methods and fields of the enclosing class | Limited to what’s in scope; no direct access to this methods. |
2. Lambdas vs Streams API
Factors | Lambda Expression | Streams API |
Purpose | To provide a concise syntax for implementing functional interfaces. | To process sequences of elements (collections) in a declarative way. |
Syntax | parameters -> expression or parameters -> { statements } | collection.stream().operation(...) |
Use Case | Implement functional interfaces such as Runnable, Comparator, etc. | Perform operations on collections like filter, map, reduce, etc. |
Readability | Improves readability by reducing boilerplate code for single-method interfaces. | Improves readability by allowing for a more declarative approach to processing collections. |
Scope | Primarily used within the context of functional interfaces. | Used with collections, arrays, or other data sources that can be converted to a stream. |
Performance | Direct method implementation, generally efficient. | It can be optimized by the JVM and supports parallel processing for better performance on large datasets. |
State Management | Stateless; each lambda expression typically represents a single, isolated operation. | Stateless by default, but can be stateful in cases like distinct() or sorted(). |
Operations | Limited to the operations defined in the functional interface. | Supports a wide range of operations like filter, map, flatMap, collect, reduce, etc. |
When to Use | Use when you need to implement a functional interface, particularly for short, simple tasks. | Use when you need to process collections in a declarative, functional style, especially with multiple operations or parallel processing. |
3. Lambdas vs Method References
Factors | Lambda Expression | Method Reference |
Purpose | Provides a concise way to implement functional interfaces inline. | A shorthand for calling an existing method in a lambda expression. |
Syntax | parameters -> expression or parameters -> { statements } | ClassName :: methodName or object :: methodName |
Use Case | Used when custom logic needs to be written inline. | Used when an existing method can be referenced directly. |
Readability | More flexible but can be slightly verbose for simple operations. | More concise and readable, especially when referencing simple methods. |
When to use | Use when you need to write inline logic or call methods with parameters. | Use when you are calling an existing method, simplifying the code. |
Flexibility | More flexible; allows writing custom code within the lambda body. | Less flexible; can only be used to reference existing methods. |
Constructor Support | No direct support for constructors; must call them explicitly. | Less flexible; can only be used to reference existing methods. |
Chaining | Can be used in chaining (e.g., in stream operations). | Can also be used in chaining, often improving readability. |
Performance | Performance is similar to method references but might include extra overhead. | Slightly better performance as it’s a direct method reference. |
Conclusion
In conclusion, we have explored Lambda expressions in Java, which simplify the implementation of functional interfaces and reduce boilerplate code. They enhance code readability, making it easier to work with collections and functional operations like filtering and mapping. Lambda expressions are now a key tool for writing cleaner, more efficient Java code. If you are a newbie to Java Programming, we recommend you to enroll in our Java Full Stack Course to get a better understanding with a step-by-step comprehensive guide to Full Stack Java Development.
FAQs
- No state management (stateless).
- Only works with functional interfaces (one abstract method).
- Limited to simple expressions or blocks of code.
- Difficult to debug due to concise syntax.
- Type inference can be ambiguous in complex cases.