Что означает ключевое слово final и к каким сущностям оно применимо в Java
Что проверяют
Знаете ли вы все места, где применимо final, и различаете ли неизменность ссылки и неизменность объекта. Уровнем выше спросят про гарантии видимости final-полей между потоками.
Как отвечать
final это запрет повторного присваивания или переопределения. Применяется к четырём вещам:
- локальная переменная и параметр метода: значение присваивается один раз;
- поле: инициализируется при объявлении, в блоке инициализации или в конструкторе, дальше не меняется;
- метод: нельзя переопределить в наследнике;
- класс: нельзя наследовать, так сделаны
String,Integer, классы даты и времени.
Дальше стоит добавить три уточнения, которые отличают уверенный ответ. Первое: у ссылочного поля final фиксирует ссылку, а не содержимое, и final List<String> items спокойно пополняется, неизменяемость делается иначе, копией в конструкторе и List.copyOf. Второе: поля интерфейса и так неявно public static final, а константа времени компиляции это static final примитив или строка, заданная литералом. Третье: корректно проинициализированные final-поля видны другим потокам сразу после выхода из конструктора, без синхронизации, поэтому неизменяемые объекты безопасно публиковать.
Полезно упомянуть effectively final: локальную переменную, которую ни разу не переприсвоили, можно использовать в лямбде и анонимном классе, писать слово final для этого не требуется.
Где спотыкаются
Говорят, что final делает объект неизменяемым. Один пример с пополняемой final-коллекцией снимает вопрос, а вместе с ним и половину ответа.
Повторяют миф про ускорение. Компилятор внутри виртуальной машины и так подставляет тело вызова, у которого на практике одна реализация. Настоящая польза final это гарантии модели памяти и явный контракт для читателя.
Плывут на границах применения. final и abstract несовместимы ни для класса, ни для метода, приватный метод и так не переопределяется, поле обязано быть проинициализировано к концу каждого конструктора, а обещанная в шпаргалках неприменимость к интерфейсам верна только для самого интерфейса, но не для его полей.