Lambda expressions (since Java 8) provide a concise way to represent anonymous functions. They are the foundation of functional programming in Java and work with the Stream API and functional interfaces.

Syntax

  (parameters) -> expression
(parameters) -> { statements; }
  

Examples:

  // No parameters
Runnable r = () -> System.out.println("Hello");

// One parameter (parentheses optional)
Consumer<String> printer = s -> System.out.println(s);

// Multiple parameters
BinaryOperator<Integer> add = (a, b) -> a + b;

// Block body
Comparator<String> byLength = (a, b) -> {
    int diff = a.length() - b.length();
    return Integer.compare(diff, 0);
};
  

Functional Interfaces

A lambda can only be assigned to a functional interface — an interface with exactly one abstract method:

  @FunctionalInterface
public interface Calculator {
    int calculate(int a, int b);
}

Calculator multiply = (a, b) -> a * b;
System.out.println(multiply.calculate(6, 7)); // 42
  

Common Use Cases

Collections

  List<String> names = List.of("Alice", "Bob", "Charlie");

names.forEach(name -> System.out.println(name));
names.sort((a, b) -> a.compareToIgnoreCase(b));
names.removeIf(name -> name.startsWith("A"));
  

Threads

  new Thread(() -> System.out.println("Running in thread")).start();
  

Event Handlers

  button.setOnAction(event -> System.out.println("Clicked!"));
  

Method References

Shorthand when a lambda simply calls an existing method:

Type Syntax Example
Static method ClassName::method Integer::parseInt
Instance method instance::method System.out::println
Instance method of arbitrary object ClassName::method String::compareToIgnoreCase
Constructor ClassName::new ArrayList::new
  List<String> names = List.of("alice", "bob");
names.stream()
     .map(String::toUpperCase)
     .forEach(System.out::println);
  

Variable Capture

Lambdas can access effectively final local variables and instance fields:

  int factor = 10;
Function<Integer, Integer> multiply = n -> n * factor;
// factor = 20; // Compile error — factor is no longer effectively final
  

Lambdas cannot modify local variables from the enclosing scope.

Lambda vs Anonymous Inner Class

  // Anonymous inner class
Runnable r1 = new Runnable() {
    public void run() {
        System.out.println("Hello");
    }
};

// Lambda
Runnable r2 = () -> System.out.println("Hello");
  

Lambdas do not create a separate .class file per instance (uses invokedynamic), making them more efficient for short callbacks.

Best Practices

  • Keep lambdas short — extract complex logic into named methods
  • Prefer method references when they improve readability
  • Use meaningful parameter names for multi-parameter lambdas
  • Avoid side effects inside lambdas passed to Stream operations when possible