Что такое иммутабельный объект
Что проверяют
Знаете ли вы рецепт целиком, а не только слово final, и понимаете ли, ради чего он нужен. Ответ обычно разворачивают в сторону многопоточности, потому что именно там неизменяемость даёт самую заметную выгоду.
Как отвечать
Рецепт: класс final или конструктор закрыт от наследования; все поля private final; сеттеров и любых меняющих состояние методов нет; изменяемые объекты, пришедшие в конструктор, копируются внутрь; наружу отдаются копии или неизменяемые представления; ссылка на this не утекает из конструктора.
Важная деталь про потоки: корректно заполненное final-поле видно другим потокам сразу после завершения конструктора, без дополнительной синхронизации. Это гарантия модели памяти, и именно поэтому неизменяемые объекты свободно передают между потоками и кешируют.
public final class Route {
private final List<String> stops;
public Route(List<String> stops) {
this.stops = List.copyOf(stops); // копия, а не чужая ссылка
}
public List<String> stops() {
return stops;
}
}Запись record закрывает часть пунктов автоматически: поля неизменяемы, сеттеров нет, сравнение по значению готово. Копирование изменяемых аргументов остаётся на разработчике, для этого и существует компактный конструктор.
Где спотыкаются
Говорят, что поставили final на поля и объект стал неизменяемым, а в полях лежат список или изменяемая дата. Ссылку переприсвоить нельзя, содержимое меняется свободно.
Путают неизменяемую копию с неизменяемым представлением. Обёртка над чужим списком запрещает изменения через себя, но исходная ссылка осталась у вызывающего кода, и через неё содержимое меняется. Отвязывает только копия.
Заучивают правило про final для каждого поля как абсолютное и теряются, когда спрашивают про ленивое вычисление. Значение, посчитанное из неизменяемых полей и не меняющее наблюдаемое состояние, кешировать допустимо, так устроен кеш хеш-кода строки, но это аккуратная оптимизация с оглядкой на видимость, а не рядовой приём.