Notifications

No notifications

/Phase 4

Lambdas & Functional Interfaces

A lambda is a short way to write an anonymous function. Java added them in 8 to enable functional-style code (streams, callbacks, event handlers). Every lambda is really an instance of a *functional interface* — an interface with exactly one abstract method. The standard set lives in java.util.function (Function, Predicate, Consumer, Supplier, …).

On this page

Detailed Theory

# Lambdas & Functional Interfaces

Lambda syntax — three flavours

() -> 42                                  // no params
x -> x * x                                // single param, no parens needed
(int a, int b) -> a + b                   // explicit types
(a, b) -> {                               // multi-line body
    int sum = a + b;
    return sum;
}

Functional interface

An interface with exactly one abstract method. @FunctionalInterface makes the compiler verify it.

@FunctionalInterface
interface Greeter {
    String greet(String name);            // the single abstract method
}

Greeter g = name -> "hi " + name; // lambda assigned to it System.out.println(g.greet("alice")); // hi alice

The standard functional interfaces (java.util.function)

interfaceshapeexample
FunctionT → RString::length
PredicateT → booleans -> s.isEmpty()
ConsumerT → voidSystem.out::println
Supplier() → T() -> new ArrayList<>()
BiFunction(T, U) → R(a, b) -> a + b
UnaryOperatorT → Tx -> x.toUpperCase()
BinaryOperator(T, T) → TInteger::sum

Method references — even shorter syntax

formexampleequivalent lambda
static methodInteger::parseInts -> Integer.parseInt(s)
instance of objectout::printlnx -> out.println(x)
instance of typeString::lengths -> s.length()
constructorArrayList::new() -> new ArrayList<>()

Where lambdas show up

list.forEach(System.out::println);
list.removeIf(s -> s.isBlank());
list.sort(Comparator.comparingInt(String::length));
new Thread(() -> System.out.println("running")).start();
button.addActionListener(e -> doStuff());

Capturing variables (effectively final)

A lambda can use local variables only if they are *effectively final* — never reassigned after creation.
int multiplier = 10;
Function<Integer,Integer> times = x -> x * multiplier;   // ok
// multiplier = 20;   <-- would make the line above fail to compile

Composition

Function<Integer,Integer> add1 = x -> x + 1;
Function<Integer,Integer> dbl  = x -> x * 2;

add1.andThen(dbl).apply(3); // (3+1)*2 = 8 add1.compose(dbl).apply(3); // (3*2)+1 = 7

Predicate<String> nonEmpty = s -> !s.isEmpty(); Predicate<String> short_ = s -> s.length() < 5; nonEmpty.and(short_).test("hi"); // true nonEmpty.or(short_).negate().test(""); // false