Skip to content

Borrowing variables: the effectively final rule

☕ Java 17 · How Lambdas See the Variables Around Them · 3/4

A lambda can use variables from the class or method around it, but not all of them on the same terms.

Variable Can the lambda use it?
Instance and static fields ✅ always, and it can even change them
Method parameters ✅ only if final or effectively final
Local variables ✅ only if final or effectively final

Effectively final

A variable is effectively final when it's assigned once and never reassigned, before or after the lambda, even without the final keyword.

Quick test: add final in front of it in your head. If the code still compiles, it's effectively final.

The rule in action

import java.util.function.Consumer;

public class Player {
    private String quality = "HD";

    public void start(String title) {
        String device = "TV";
        Consumer<String> show = u ->
            System.out.println(u + ": " + title + " on " + device + " in " + quality);  // ❌ device
        quality = "4K";   // ✅ a field
        device = "Phone"; // makes device not effectively final
        show.accept("Ana");
    }
}
  • quality is a field: changing it is fine. The lambda reads the current value when it runs.
  • title is never reassigned, so it's effectively final.
  • device is reassigned, so it loses its status. The compiler reports the error on the lambda line, not on the reassignment:
error: local variables referenced from a lambda expression must be final or effectively final

Remove device = "Phone"; and it compiles and prints Ana: Dune on TV in 4K for start("Dune"). Note the 4K: the field changed before the lambda ran.

Changing a captured local inside the lambda is illegal too: u -> device = "Phone" doesn't compile. A lambda can read a captured local, never modify it.

Why the rule exists

A lambda can run later, long after the line that created it (deferred execution). Java copies the value of the local variable into the lambda, so that value must not change behind its back. Fields don't have this problem: the lambda reads them through the object each time.

Time to apply all three places in a refactoring.

💻 Code: Java17_Part_8_Functional_Programming on GitHub

← Variables inside the block · Next: the lab →