Что такое effectively final переменная в Java и зачем это понятие нужно?

«Что такое effectively final переменная в Java и зачем это понятие нужно?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Effectively final — это переменная, которая не объявлена как final, но никогда не изменяется после инициализации. Это понятие критически важно для работы с лямбда-выражениями и анонимными классами, поскольку они могут захватывать из окружающей области видимости только final или effectively final переменные.

Почему такое ограничение?

  1. Потокобезопасность: Захваченная копия переменной должна быть неизменяемой, чтобы избежать race condition между внешним потоком и лямбдой.
  2. Семантика захвата: Лямбда может выполняться позже и в другом потоке. Если бы переменная могла меняться, её значение стало бы непредсказуемым.

Пример:

int threshold = 100; // effectively final
List<String> filtered = items.stream()
    .filter(item -> item.length() > threshold) // Захват переменной
    .toList();

// threshold = 200; // Ошибка компиляции, если раскомментировать!
//                 // Переменная перестанет быть effectively final.

Сравнение с явным final:

final int x = 10;          // Явно final
int y = 20;                // Effectively final (не меняется)
int z = 30;
z = 40;                    // z НЕ effectively final

Runnable r1 = () -> System.out.println(x + y); // OK
Runnable r2 = () -> System.out.println(z);     // Ошибка компиляции!

Практическое значение:

  • Позволяет писать более чистый код, не загромождая его ключевым словом final.
  • Компилятор сам определяет, можно ли безопасно захватить переменную.
  • Применяется также к анонимным классам (до Java 8) и локальным классам.

Исключение для изменяемых объектов: Важно: правило касается ссылки, а не состояния объекта.

List<String> list = new ArrayList<>(); // effectively final ссылка
list.add("item"); // OK — меняется состояние объекта, а не сама ссылка `list`

Runnable r = () -> System.out.println(list.size()); // Корректно

Итог: Концепция effectively final упрощает синтаксис, обеспечивая при этом безопасность доступа к данным из замыканий в многопоточной среде.