Learn Java 25 lambda expressions
Java lambda expressions are much easier to understand once you stop treating their syntax as something mysterious. A lambda is simply a compact implementation of the single abstract method defined by a functional interface.
A Java lambda has three basic pieces:
- A parameter list
- The
->arrow operator - An expression or block that implements the method
For this Java 25 example, start with a small functional interface:
@FunctionalInterface
interface SingleArgument {
void accept(String text);
}The @FunctionalInterface annotation is not mandatory, but it is a useful modern Java practice. The compiler verifies that the interface contains exactly one abstract method suitable for lambda implementation.
A simple Java lambda
The following lambda receives a String and prints it with Java 25's IO.println method:
SingleArgument printer = text -> IO.println(text);
printer.accept("Java 25 lambdas are concise.");The compiler infers that text is a String from the SingleArgument interface. There is normally no reason to repeat the parameter type.
Explicit parameter types
You can explicitly declare the parameter type when it improves clarity:
SingleArgument printer =
(String text) -> IO.println(text);For a single inferred parameter, parentheses can be omitted. If you explicitly provide its type, the parentheses are required.
Block lambda expressions
An expression lambda can contain a single expression. When the implementation needs multiple statements, use a block lambda with braces:
SingleArgument compactPrinter = text -> {
var compact = text.replaceAll("\\s+", "");
IO.println(compact);
};
compactPrinter.accept("Java lambda syntax is easy");This prints:
JavalambdasyntaxiseasyLambda expressions with multiple parameters
A functional interface can define a method with multiple parameters. The lambda must provide the same number of compatible parameters.
@FunctionalInterface
interface PieOrder {
void print(String name, int slices);
}The implementation can be written with explicit types:
PieOrder verbose = (String name, int slices) -> {
IO.println(name + " wants " + slices + " slices of pie.");
};Or, more idiomatically, let Java infer the types:
PieOrder concise =
(name, slices) ->
IO.println(name + " wants " + slices + " slices of pie.");
concise.print("Cameron", 3);Lambdas that return values
Many useful functional interfaces return a value. Java's standard java.util.function package already provides interfaces for common cases, so you usually do not need to create your own.
import java.util.function.Function;
Function<String, Integer> length =
text -> text.length();
IO.println(length.apply("Java 25"));For a single-expression lambda, the result is returned automatically. A block lambda requires an explicit return statement when its functional method returns a value:
Function<String, Integer> trimmedLength = text -> {
var trimmed = text.trim();
return trimmed.length();
};Method references
If a lambda merely calls an existing method, a method reference can often make the intent even clearer. For example, Java 25's IO.println can be referenced directly:
SingleArgument printer = IO::println;
printer.accept("Hello from a method reference.");The lambda text -> IO.println(text) and the method reference IO::println express the same basic operation here.
Complete Java 25 lambda example
Java 25 also supports compact source files, which makes small examples especially convenient. The following complete example avoids the ceremony of an explicit class declaration and uses the Java 25 IO class for console output:
import java.util.function.Function;
void main() {
SingleArgument printer = IO::println;
printer.accept("Learn Java 25 lambda syntax.");
SingleArgument noWhitespace = text -> {
var compact = text.replaceAll("\\s+", "");
IO.println(compact);
};
noWhitespace.accept("Lambda expressions are compact.");
PieOrder order =
(name, slices) ->
IO.println(name + " wants " + slices + " slices of pie.");
order.print("Cameron", 3);
order.print("Callie", 4);
Function<String, Integer> length = String::length;
IO.println("Length: " + length.apply("Java 25"));
}
@FunctionalInterface
interface SingleArgument {
void accept(String text);
}
@FunctionalInterface
interface PieOrder {
void print(String name, int slices);
}The important point is that lambdas did not fundamentally change in Java 25. What has improved is the surrounding Java language. Type inference, standard functional interfaces, method references, compact source files and the IO class make small functional-programming examples cleaner than the older System.out-heavy style.
You can find additional Java examples on GitHub.
Cameron McKenzie is an AWS Certified AI Practitioner, Machine Learning Engineer, Solutions Architect and author of many popular books in the software development and Cloud Computing space. His growing YouTube channel training devs in Java, Spring, AI and ML has well over 30,000 subscribers.