JavaLambda Expressions

Lambda Expressions

Introduced in Java 8, a lambda expression is a compact way to write an implementation of an interface that has exactly one abstract method, without the ceremony of a full anonymous class. A lambda is essentially an anonymous, unnamed function you can pass around as a value.

Before and after: anonymous classes vs. lambdas

Before Java 8, implementing a single-method interface inline meant writing a full anonymous class — a lot of boilerplate for what is conceptually a small piece of behavior. If you have already read the Anonymous Classes page, the example below will look familiar.

Before — an anonymous class

Java
Comparator<String> byLength = new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return Integer.compare(a.length(), b.length());
    }
};

After — the equivalent lambda expression

Java
Comparator<String> byLength = (a, b) -> Integer.compare(a.length(), b.length());

Both versions do exactly the same thing: they implement Comparator<String>'s single abstract method, compare(). The lambda just skips the class name, the method name, the @Override annotation, and most of the punctuation — the compiler infers all of that from the context (here, the fact that byLength is declared as a Comparator<String>).

Lambda syntax

A lambda has the general shape (parameters) -> body. The body can be a single expression, whose value is automatically returned, or a block of statements in braces with an explicit return.

Lambda syntax variations

Java
// No parameters
Runnable sayHi = () -> System.out.println("Hi!");

// One parameter — parentheses are optional with a single inferred-type parameter
Consumer<String> printer = name -> System.out.println(name);

// Multiple parameters, expression body
Comparator<Integer> byValue = (a, b) -> a - b;

// Block body with an explicit return
Function<Integer, String> describe = (n) -> {
    if (n % 2 == 0) {
        return n + " is even";
    }
    return n + " is odd";
};
Only for functional interfaces

A lambda expression can only implement an interface with exactly one abstract method — these are called functional interfaces, and the next page covers them in depth. If an interface declares two or more abstract methods, the compiler has no way to know which one the lambda is meant to implement, so lambdas simply are not allowed there.

The effectively-final capture rule
A lambda can reference local variables from its enclosing scope, but only if those variables are **final** or **effectively final** — meaning they are never reassigned after their initial assignment. This is different from instance or static fields, which lambdas can freely read *and* write.

Broken — count is reassigned, so it cannot be captured

Java
int count = 0;
Runnable increment = () -> {
    count++;              // compile-time error: count is not effectively final
    System.out.println(count);
};

Fixed — use a mutable holder instead of reassigning a local

Java
int[] count = { 0 };   // an array reference is itself never reassigned
Runnable increment = () -> {
    count[0]++;           // mutating the array's contents is fine
    System.out.println(count[0]);
};

increment.run();  // 1
increment.run();  // 2

The array itself (count) is never reassigned — only its contents change — so it stays effectively final and the lambda is allowed to capture it. This is a common workaround, though in real code an instance field or an AtomicInteger is usually a cleaner choice.

Note
The effectively-final rule exists because a lambda may run later, possibly on a different thread, after the method that created it has already returned. Java captures local variables **by value** at the time the lambda is created, so allowing reassignment afterward would create a confusing mismatch between the captured value and the variable's current value.
  • A lambda expression (params) -> body is shorthand for implementing a single-method interface inline.

  • Lambdas can only implement functional interfaces — interfaces with exactly one abstract method.

  • The body can be a single expression (implicitly returned) or a block with an explicit return.

  • A lambda can only capture local variables that are final or effectively final — never reassigned.