Achtergrond:
Voor Java 8 moesten variabelen uit de externe scope die in een interne of anonieme klasse werden gebruikt, expliciet als final worden gedeclareerd. In Java 8 is deze eis versoepeld: nu kan een variabele als effectively final worden beschouwd als deze niet daadwerkelijk wordt gewijzigd.
Probleem:
Lambda-expressies en interne klassen gebruiken closures over variabelen uit de externe context. Wanneer dergelijke variabelen echter van waarde veranderen, ontstaan verwarring en onjuiste gedragingen — het is niet duidelijk welke waarde moet worden gebruikt.
Oplossing:
De compiler staat alleen het gebruik van een variabele in een interne klasse of lambda toe als deze effectively final is — dat wil zeggen, nooit is gewijzigd na de initialisatie, ook al is deze niet expliciet als final gedeclareerd.
Codevoorbeeld:
public void demo() { int x = 10; Runnable r = () -> System.out.println(x); // x is effectively final r.run(); }
Als je probeert x te wijzigen:
public void demo() { int x = 10; x = 20; // nu is x niet effectively final Runnable r = () -> System.out.println(x); // Compilatiefout }
Belangrijkste kenmerken:
Kun je wijzigbare objecten gebruiken als de referentie effectively final is?
Ja, als de referentie niet verandert, maar het object waar deze referentie naar verwijst verandert — dat is toegestaan. Bijvoorbeeld,
List<String> list = new ArrayList<>(); list.add("A"); Runnable r = () -> System.out.println(list.get(0)); // OK list = new ArrayList<>(); // Dit zal een compilatiefout geven
Kun je een variabele als final declareren en toch de inhoud van het object wijzigen?
Ja. Final verwijst naar de referentie, niet naar de inhoud van het object. Het is toegestaan om de status van het object via de referentie te wijzigen. Bijvoorbeeld,
final List<Integer> nums = new ArrayList<>(); nums.add(5); // OK nums = new ArrayList<>(); // Fout
Kun je methode-argumentvariabelen in lambda's gebruiken?
Ja, als ze ook effectively final zijn — dat wil zeggen, niet worden gewijzigd binnen de methode na initialisatie.
In de methode worden variabelen gebruikt die per ongeluk worden overschreven na het maken van de lambda, waardoor de programma niet compileert en tijd wordt verspild om de oorzaak te achterhalen.
Voordelen:
Nadelen:
Een ontwikkelaar gebruikt een incrementele waarde via AtomicInteger (of een ander holder-object), dat de referentie opslaat in plaats van de waarde, en zorgt voor de correcte werking van lambda's, zelfs als het nodig is om de teller binnen de lambda-expressie te wijzigen.
Voordelen:
Nadelen: