Вопрос с собеседования · Java

Конструктор сохранил переданный List по ссылке (this.items = items). Почему объект всё равно изменяем?

MiddleООП
Коротко

Поле хранит чужую ссылку: вызывающий меняет список снаружи, и «неизменяемый» объект меняется. final запрещает переприсвоить поле, но не менять содержимое. Нужна защитная копия на входе (List.copyOf) и неизменяемое представление на выходе.

  • Копировать до валидации — иначе гонка «проверил, а потом подменили»
  • Collections.unmodifiableList без копии не защищает от исходной ссылки
  • Дата: java.util.Date изменяема, LocalDate — нет

А ещё List.copyOf запрещает null-элементы

Подробно

final у ссылки запрещает переприсвоить поле, но не делает сам список неизменяемым. Если сохранить переданную ссылку, у вызывающего остаётся та же ссылка, и он поменяет «внутренности» вашего объекта — неизменяемость иллюзорна. Поэтому изменяемые входные данные копируют в конструкторе (List.copyOf(events)), а в геттере возвращают копию или неизменяемое представление. Это defensive copy.

Что хотят услышать

  • Почему final недостаточно.
  • Копии на входе и выходе.

Частые ошибки

  • Думать, что final-ссылка делает содержимое неизменяемым.
  • Возвращать внутренний список из геттера напрямую.

Проверьте себя

Один верный вариант. Подумайте, потом откройте ответ.

Показать варианты и ответ
  • ✓Вызывающий меняет тот же список снаружи — нужна защитная копия на входе
  • ✗Достаточно пометить поле final — тогда и содержимое списка станет неизменяемым
  • ✗List нельзя хранить в final-поле, поэтому неизменяемость ломается
  • ✗Проблема возникает, только если список пустой

Что спросят дальше

  • Что делает List.copyOf на входе?
  • Почему копию нужно делать и в геттере?

Разбор темы

Связанные вопросы

Партнёр платформы

Разбор собеседования с sotto

После собеседования sotto разбирает разговор: оценка, каждый вопрос — хорошо, частично или слабо, чего не хватило в ответе и над чем поработать к следующему разу. Первые 30 минут бесплатно.

Разбор собеседования