Java Lambda Expressions
Last modified October 13, 2026
Lambda expressions, introduced in Java 8, bring functional programming to Java. A lambda expression is an anonymous function: it has parameters and a body, but no name, and it provides an inline implementation of a functional interface. This tutorial covers the syntax, the most common functional interfaces, and practical use with arrays, collections, and streams.
The basic syntax of a lambda expression is as follows:
(parameters) -> expression
(parameters) -> { statements; }
The syntax can be abbreviated in several ways:
- Parameter types are optional; the compiler infers them from the context.
- Parentheses around a single parameter are optional, provided its type is not declared. They are required for zero or multiple parameters.
- Curly braces are optional when the body is a single expression. The value
of that expression is returned automatically, so no
returnkeyword is needed. - Curly braces are required when the body contains multiple statements. A
lambda that returns a value must then use an explicit
return.
Sorting an Array
Sorting arrays is a common task in programming, and lambda expressions make it easier by providing a concise way to define comparison logic. The following example demonstrates sorting an array of strings alphabetically using a lambda expression.
package com.zetcode;
import java.util.Arrays;
public class LambdaSortEx {
public static void main(String[] args) {
String[] words = { "kind", "massive", "atom", "car", "blue" };
Arrays.sort(words, (s1, s2) -> s1.compareTo(s2));
System.out.println(Arrays.toString(words));
}
}
This example demonstrates sorting an array of strings using the
Arrays.sort method with a lambda expression. The lambda expression
(s1, s2) -> s1.compareTo(s2) defines the comparison logic,
resulting in an alphabetically sorted array.
$ java com/zetcode/LambdaSortEx.java [atom, blue, car, kind, massive]
Functional Interfaces and Lambda Expressions
Functional interfaces, which contain a single abstract method, are the
foundation for lambda expressions in Java. Lambda expressions provide a compact
way to implement these interfaces. The optional @FunctionalInterface
annotation asks the compiler to verify that an interface has exactly one
abstract method. An interface may still contain default and static methods.
Implementing a Greeting Service
Creating custom functional interfaces allows developers to define specific behaviors. This example shows how to use a lambda expression to implement a simple greeting service that outputs messages to the console.
package com.zetcode;
@FunctionalInterface
interface GreetingService {
void greet(String message);
}
public class LambdaGreetingEx {
public static void main(String[] args) {
GreetingService gs = msg -> System.out.println(msg);
gs.greet("Good night");
gs.greet("Hello there");
}
}
In this example, a GreetingService functional interface is defined
with a single greet method, marked with
@FunctionalInterface. A lambda expression implements this
interface, printing messages to the console when the greet method
is invoked.
$ java com/zetcode/LambdaGreetingEx.java Good night Hello there
Common Functional Interfaces
The java.util.function package provides a set of built-in
functional interfaces, such as Function, Consumer,
Supplier, and Predicate. These interfaces support
common functional programming patterns, making lambda expressions even more
versatile.
Using the Function Interface
The Function interface is ideal for operations that take an input
and produce an output. This example uses a lambda expression to define a
function that computes the square of an integer.
package com.zetcode;
import java.util.function.Function;
public class LambdaFunctionEx {
public static void main(String[] args) {
Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(5));
}
}
This example uses the Function interface to define a lambda
expression that computes the square of an integer. The apply method
executes the lambda, returning the result.
$ java com/zetcode/LambdaFunctionEx.java 25
Filtering Data with Streams
Java streams, combined with lambda expressions, provide a powerful way to process collections of data. This example demonstrates filtering a list of objects based on a condition defined by a lambda expression.
package com.zetcode;
import java.util.List;
public class LambdaFilterEx {
public static void main(String[] args) {
List<User> persons = List.of(
new User("Jack", "jack234@gmail.com"),
new User("Peter", "pete2@post.com"),
new User("Lucy", "lucy17@gmail.com"),
new User("Robert", "bob56@post.com"),
new User("Martin", "mato4@imail.com")
);
List<User> result = persons.stream()
.filter(person -> person.email().endsWith("@post.com"))
.toList();
result.forEach(p -> System.out.println(p.name()));
}
}
record User(String name, String email) {}
This example uses a lambda expression within a stream to filter
User objects based on their email domain. The lambda expression in
the filter method selects users whose email ends with "@post.com",
the stream's toList method (Java 16+) collects the result, and
another lambda in forEach prints the names.
$ java com/zetcode/LambdaFilterEx.java Peter Robert
Mapping Data with Streams
The map method in Java streams allows developers to transform data
using lambda expressions. This example shows how to convert a list of strings
into a list of their lengths.
package com.zetcode;
import java.util.List;
public class LambdaMapEx {
public static void main(String[] args) {
List<String> words = List.of("apple", "banana", "cherry");
List<Integer> lengths = words.stream()
.map(word -> word.length())
.toList();
System.out.println(lengths);
}
}
This example demonstrates the use of a lambda expression with the
map method to transform a list of strings into a list of their
lengths. The lambda word -> word.length() computes the length of
each word.
$ java com/zetcode/LambdaMapEx.java [5, 6, 6]
Using Consumer Interface
The Consumer interface is used for operations that take an input
and produce no output, such as printing or modifying data. This example uses a
lambda expression to print strings in uppercase.
package com.zetcode;
import java.util.function.Consumer;
public class LambdaConsumerEx {
public static void main(String[] args) {
Consumer<String> printUpperCase = text -> System.out.println(text.toUpperCase());
printUpperCase.accept("hello");
printUpperCase.accept("java lambda");
}
}
This example uses the Consumer interface to define a lambda
expression that converts a string to uppercase and prints it. The
accept method triggers the lambda's execution.
$ java com/zetcode/LambdaConsumerEx.java HELLO JAVA LAMBDA
Using Supplier Interface
The Supplier interface is used to generate values without taking
any input. This example demonstrates a lambda expression that produces random
greetings.
package com.zetcode;
import java.util.function.Supplier;
public class LambdaSupplierEx {
public static void main(String[] args) {
Supplier<String> randomGreeting = () -> {
String[] greetings = {"Hello", "Hi", "Greetings"};
return greetings[(int) (Math.random() * greetings.length)];
};
System.out.println(randomGreeting.get());
System.out.println(randomGreeting.get());
}
}
This example uses the Supplier interface to create a lambda
expression that returns a random greeting. The get method retrieves
the value produced by the lambda. Because the greetings are chosen randomly,
the output differs between runs.
$ java com/zetcode/LambdaSupplierEx.java Hi Greetings
Combining Predicates
The Predicate interface is used for testing conditions. This
example shows how to combine multiple predicates using logical operations to
filter data.
package com.zetcode;
import java.util.List;
import java.util.function.Predicate;
public class LambdaPredicateEx {
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8);
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<Integer> isGreaterThanFour = n -> n > 4;
List<Integer> result = numbers.stream()
.filter(isEven.and(isGreaterThanFour))
.toList();
System.out.println(result);
}
}
This example combines two Predicate lambda expressions to filter a
list of numbers, selecting only even numbers greater than four. The
and method combines the predicates logically.
$ java com/zetcode/LambdaPredicateEx.java [6, 8]
Capturing Variables
A lambda expression can use local variables from its enclosing scope, but only
if they are final or effectively final, meaning they are never
reassigned after initialization. Unlike an anonymous class, a lambda does not
introduce a new scope: this inside a lambda refers to the enclosing
instance.
package com.zetcode;
import java.util.List;
public class LambdaCaptureEx {
public static void main(String[] args) {
String prefix = "Hello, ";
List<String> names = List.of("Jack", "Lucy", "Peter");
names.forEach(name -> System.out.println(prefix + name));
}
}
The lambda captures the prefix variable. Assigning a new value to
prefix anywhere in the method would cause a compile-time error.
$ java com/zetcode/LambdaCaptureEx.java Hello, Jack Hello, Lucy Hello, Peter
Method References
When a lambda only calls an existing method, a method reference is a shorter
alternative. The :: operator refers to static methods, instance
methods, and constructors. In the example below,
String::toUpperCase is equivalent to
s -> s.toUpperCase() and System.out::println is
equivalent to s -> System.out.println(s).
package com.zetcode;
import java.util.List;
public class MethodRefEx {
public static void main(String[] args) {
List<String> words = List.of("kind", "massive", "atom", "car", "blue");
words.stream()
.map(String::toUpperCase)
.sorted(String::compareTo)
.forEach(System.out::println);
}
}
The example converts the words to uppercase, sorts them, and prints each one.
$ java com/zetcode/MethodRefEx.java ATOM BLUE CAR KIND MASSIVE
Summary
Lambda expressions provide concise implementations of functional interfaces.
Combined with the built-in interfaces in java.util.function,
method references, and streams, they make code for sorting, filtering, and
mapping data shorter and easier to read. Prefer short lambdas; if a body grows
beyond a few lines, move it into a named method and use a method reference.
Source
Java Lambda Expressions Documentation
Author
List all Java tutorials.