Historique de la question :
Avant Java 8, pour utiliser des variables d'un contexte externe dans une classe interne ou une classe anonyme, ces variables devaient obligatoirement être déclarées comme final. Dans Java 8, cette exigence a été assouplie : maintenant, une variable peut ne pas être déclarée comme final, à condition qu'en fait elle ne soit pas modifiée (effectively final).
Problème :
Les expressions lambda et les classes internes utilisent des fermetures autour des variables du bloc externe. Cependant, si ces variables changent de valeur, cela provoque de la confusion et un comportement incorrect : il est impossible de comprendre quelle valeur utiliser.
Solution :
Le compilateur autorise l'utilisation d'une variable dans une classe interne ou une lambda uniquement si elle est effectively final — c'est-à-dire jamais modifiée après initialisation, même si elle n'est pas explicitement déclarée comme final.
Exemple de code :
public void demo() { int x = 10; Runnable r = () -> System.out.println(x); // x — effectively final r.run(); }
Si on essaie de modifier x :
public void demo() { int x = 10; x = 20; // maintenant x n'est pas effectively final Runnable r = () -> System.out.println(x); // Erreur de compilation }
Points clés :
Peut-on utiliser des objets mutables si la référence elle-même est effectively final ?
Oui, si la référence ne change pas, mais que l'objet référencé change — c'est permis. Par exemple,
List<String> list = new ArrayList<>(); list.add("A"); Runnable r = () -> System.out.println(list.get(0)); // OK list = new ArrayList<>(); // Si c'est le cas, une erreur de compilation se produira
Peut-on déclarer une variable comme final, puis modifier le contenu de l'objet ?
Oui. final se réfère à la référence, pas au contenu de l'objet. Modifier l'état de l'objet via la référence est autorisé. Par exemple,
final List<Integer> nums = new ArrayList<>(); nums.add(5); // OK nums = new ArrayList<>(); // Erreur
Peut-on utiliser des variables-arguments de méthode dans des lambdas ?
Oui, si elles sont également effectively final — c'est-à-dire non modifiées à l'intérieur de la méthode après initialisation.
Des variables dans une méthode sont accidentellement réécrites après la création de la lambda, ce qui empêche la compilation et entraîne une perte de temps pour en comprendre la raison.
Avantages :
Inconvénients :
Le développeur utilise une valeur incrémentable via AtomicInteger (ou un autre objet holder) qui conserve la référence et non la valeur, assurant le bon fonctionnement des lambdas, même si le compteur doit être modifié à l'intérieur de l'expression lambda.
Avantages :
Inconvénients :