Modern Java

47. Lambdas vs Anonymous Classes

Lambdas are concise functional interface implementations; they cannot capture mutable local variables (only effectively-final), have no this reference of their own, and should never be serialized.

Java 8+ (lambdas); Java 21 (core language)

Explanation

Lambdas replace single-method anonymous classes with terser syntax. Key differences: a lambda has no this โ€” inside a lambda, this refers to the enclosing class instance, not the lambda itself. An anonymous class creates its own this.

Effectively final capture: a lambda can capture local variables from the enclosing scope only if those variables are final or effectively final (never reassigned after initialisation). Capturing a mutable variable is a compile error. Captured object references can still have their object mutated โ€” just the reference itself must be stable.

Do not serialise lambdas: lambda serialisation is technically possible (if the target interface extends Serializable) but the serialised form is implementation-specific and brittle across JVM versions.

Example

import java.util.List;
import java.util.function.Predicate;

public class LambdasVsAnonymous {
    interface Greeter { String greet(String name); }

    String instanceField = "Hello";

    void demo() {
        String localVar = "World"; // effectively final

        // Anonymous class: has its own 'this', verbose
        Greeter anon = new Greeter() {
            @Override public String greet(String name) {
                // 'this' here refers to the anonymous class instance
                return instanceField + " from anon, " + name;
            }
        };

        // Lambda: concise, 'this' is the outer LambdasVsAnonymous instance
        Greeter lambda = name -> instanceField + " lambda, " + name; // captures 'this' implicitly

        System.out.println(anon.greet("Alice"));   // Hello from anon, Alice
        System.out.println(lambda.greet("Bob"));   // Hello lambda, Bob

        // Effectively final โ€” OK
        // localVar = "changed"; // uncommenting this causes compile error in lambda below
        Predicate<String> check = s -> s.equals(localVar); // captures effectively-final localVar
        System.out.println(check.test("World")); // true

        // Mutable state via array trick (workaround, rarely needed โ€” prefer streams)
        int[] count = {0};
        List.of("a", "b", "c").forEach(s -> count[0]++); // array ref is final, content isn't
        System.out.println("Count: " + count[0]); // 3
    }

    public static void main(String[] args) {
        new LambdasVsAnonymous().demo();
    }
}

Sources