Lambda Expressions
A Lambda Expression is a short block of code which takes in parameters and returns a value.
In Java, Lambdas are essentially shorthand syntax for creating an anonymous class that implements a Functional Interface. They allow you to treat functionality as a method argument, or code as data.
The Syntax
The syntax of a lambda expression consists of three parts:
- Parameters: A comma-separated list of formal parameters enclosed in parentheses.
- The Arrow Token:
->(often read as "becomes" or "goes to"). - The Body: The code block to be executed.
(parameters) -> { body }
Syntax Shortcuts
- If there is only one parameter, you can drop the parentheses:
param -> { body } - If the body is only a single line of code, you can drop the curly braces
{}and thereturnkeyword:(a, b) -> a + b;
Before vs After: The Runnable Interface
To understand how much boilerplate code Lambdas eliminate, let's look at the Runnable interface. Runnable is a classic Functional Interface because it only has one abstract method: void run().
Before Java 8: Anonymous Inner Classes
If you wanted to pass a custom Runnable to a Thread without creating a whole new file, you had to use an Anonymous Inner Class:
public class LabLambda1 {
public static void main(String[] args) {
// The massive boilerplate to define a single method!
Runnable myRunnable = new Runnable() {
@Override
public void run() {
System.out.println("Running in a separate thread!");
}
};
Thread t = new Thread(myRunnable);
t.start();
}
}
After Java 8: Lambda Expressions
Because the compiler knows that Thread expects a Runnable object, and it knows Runnable only has one method (run()), it can infer everything else!
public class LabLambda2 {
public static void main(String[] args) {
// The Lambda automatically provides the implementation for the run() method!
Runnable myRunnable = () ->
System.out.println("Running in a separate thread!");
Thread t = new Thread(myRunnable);
t.start();
}
}
Implementing Built-in Functional Interfaces
Let's look at how to use Lambda expressions to implement the core functional interfaces provided in java.util.function.
1. The Predicate (Returns boolean)
import java.util.function.Predicate;
public class LabLambda3 {
public static void main(String[] args) {
// A lambda that returns true if the string length is greater than 5
Predicate<String> isLongWord = (str) -> str.length() > 5;
System.out.println("Is 'Apple' long? " + isLongWord.test("Apple")); // false
System.out.println("Is 'Banana' long? " + isLongWord.test("Banana")); // true
}
}
2. The Consumer (Returns void)
import java.util.function.Consumer;
public class LabLambda4 {
public static void main(String[] args) {
// A lambda that just prints what it receives
Consumer<String> greeter = (name) ->
System.out.println("Hello, " + name + "!");
greeter.accept("Alice");
greeter.accept("Bob");
}
}
3. The Function (Transforms an object)
import java.util.function.Function;
public class LabLambda5 {
public static void main(String[] args) {
// A lambda that takes a String and returns its Integer length
Function<String, Integer> stringLengthFinder = (str) -> str.length();
int length = stringLengthFinder.apply("Docusaurus");
System.out.println("The length is: " + length); // 10
}
}
[!TIP] The absolute biggest use-case for Lambda expressions is passing them directly into Stream API operations, allowing you to filter, map, and process collections of data in a highly readable, declarative style!