Have you ever thought that every time you handle an interface with only one abstract method, you have to write a lot of anonymous inner class code? For example, when starting a thread or sorting collections, these places require implementing an interface with only one method, and the code always looks cumbersome. The Lambda expression introduced in Java 8 is designed to solve this “code redundancy” problem, allowing us to implement these simple functional interfaces in a more concise way.
Why Lambda Expressions Are Needed?¶
Before Java 8, if you wanted to use an interface with only one abstract method (such as Runnable or Comparator), you usually needed to write an anonymous inner class. For example, when starting a thread, the code might look like this:
// Old way: Using anonymous inner class to implement Runnable
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running!");
}
}).start();
In this code, new Runnable() {} is an anonymous inner class that includes the implementation of the run() method. However, for cases with only one line of logic, this writing method seems redundant: you need to write new Runnable(), @Override, and curly braces, which actually affects code simplicity.
The emergence of Lambda expressions aims to replace this cumbersome anonymous inner class writing with a shorter syntax.
What Are Functional Interfaces?¶
To understand Lambda expressions, you first need to understand the concept of functional interfaces: A functional interface is an interface with only one abstract method. For example:
- The Runnable interface: Only has one run() method (no parameters, no return value).
- The Comparator interface: Only has one compare(T o1, T o2) method (two parameters, returns an int).
Java 8 also provides many built-in functional interfaces (such as Consumer, Supplier, etc.), but for beginners, it’s sufficient to master basic interfaces like Runnable and Comparator first.
Basic Syntax of Lambda Expressions¶
The core syntax of a Lambda expression is “parameter list -> expression body”. It can be divided into the following cases:
1. No Parameters¶
If the abstract method of the functional interface has no parameters, the parameter list is represented by empty parentheses (), and the expression body directly writes the result. For example:
() -> System.out.println("Hello, Lambda!")
2. One Parameter¶
If there is only one parameter, the parentheses can be omitted, and only the parameter name is written. For example:
s -> System.out.println(s) // Equivalent to (String s) -> System.out.println(s)
3. Multiple Parameters¶
The parameter list is wrapped in parentheses, and the type can be omitted (inferred automatically by the compiler). For example:
(a, b) -> a + b // Equivalent to (int a, int b) -> a + b
4. Expression Body Writing¶
- Single-line expression: If the expression body has only one line of code, curly braces
{}can be omitted, and the result will be returned automatically. For example:(a, b) -> a - b. - Multi-line expression: If there are multiple lines of code, curly braces are required, and
returnmust be explicitly written. For example:
(a, b) -> {
int result = a * 2 + b;
return result;
}
Practical Demonstration: Simplifying Common Scenarios with Lambda¶
Scenario 1: Using the Runnable Interface (Thread)¶
Old way (anonymous inner class):
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread started!");
}
}).start();
After Lambda simplification:
new Thread(() -> System.out.println("Thread started!")).start();
Explanation: () represents no parameters, and the logic of run() is directly written after -> in one line.
Scenario 2: Using the Comparator Interface (Sorting)¶
To sort a list of strings by length, old way:
List<String> list = Arrays.asList("apple", "banana", "cherry");
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
After Lambda simplification:
Collections.sort(list, (a, b) -> a.length() - b.length());
Explanation: (a, b) are the two parameters of compare, and the length difference is returned directly after -> without curly braces or return.
Scenario 3: Custom Functional Interface¶
Suppose we define a simple functional interface Calculator with only one add method:
@FunctionalInterface // Ensure it's a functional interface (only one abstract method)
interface Calculator {
int add(int a, int b);
}
Implement it with Lambda:
Calculator calc = (a, b) -> a + b;
System.out.println(calc.add(3, 5)); // Output: 8
Explanation: (a, b) corresponds to the parameters of add, and a + b is the return value, completed in one line of code.
Summary: Advantages of Lambda Expressions¶
Lambda expressions make the code more concise and readable, especially for handling functional interfaces with only one abstract method. Through the syntax of parameter list -> expression body, we can replace the lengthy anonymous inner class with a single line of code, significantly reducing template code and making the logic clearer.
After mastering Lambda expressions, you’ll find that scenarios like handling threads and sorting collections become extremely simple. In subsequent learning, you can also combine Java 8’s Stream API and other features to further improve code efficiency!