Какие моменты важно предусмотреть при копировании объекта иммутабельного класса?

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

Ответ

При создании копии иммутабельного объекта ключевое внимание уделяется сохранению свойства неизменяемости и предотвращению нежелательного разделения ссылок на изменяемые компоненты.

Критические аспекты:

  1. Глубокое копирование изменяемых полей: Если объект содержит ссылки на изменяемые коллекции, массивы или другие объекты, необходимо создать их новые экземпляры. Простое присваивание ссылки приведет к тому, что две «неизменяемые» коллекции будут разделять одни и те же данные.
  2. Предпочтительный способ — конструктор копирования: Самый надежный и понятный метод. Конструктор уже содержит логику создания защитных копий (defensive copies) для всех полей.
  3. Оценка необходимости копирования: Если объект состоит только из примитивов и ссылок на другие иммутабельные объекты (например, String, Integer), копирование может быть излишним — можно безопасно возвращать this.

Практический пример с конструктором копирования:

public final class ImmutableReport {
    private final String title;
    private final Date generatedDate; // Mutable!
    private final int[] data; // Mutable!

    // Основной конструктор
    public ImmutableReport(String title, Date date, int[] data) {
        this.title = title;
        this.generatedDate = new Date(date.getTime()); // Защитная копия
        this.data = data.clone(); // Защитная копия массива
    }

    // Конструктор копирования (или фабричный метод)
    public ImmutableReport copy() {
        // Используем основной конструктор, который уже делает глубокое копирование.
        return new ImmutableReport(this.title, this.generatedDate, this.data);
    }

    // Геттеры также должны возвращать защитные копии для mutable полей
    public Date getGeneratedDate() {
        return new Date(generatedDate.getTime());
    }
    public int[] getData() {
        return data.clone();
    }
}

Вывод: Копирование иммутабельного объекта должно гарантировать, что новая копия так же защищена от внешних изменений, как и оригинал.