Ответ
При создании копии иммутабельного объекта ключевое внимание уделяется сохранению свойства неизменяемости и предотвращению нежелательного разделения ссылок на изменяемые компоненты.
Критические аспекты:
- Глубокое копирование изменяемых полей: Если объект содержит ссылки на изменяемые коллекции, массивы или другие объекты, необходимо создать их новые экземпляры. Простое присваивание ссылки приведет к тому, что две «неизменяемые» коллекции будут разделять одни и те же данные.
- Предпочтительный способ — конструктор копирования: Самый надежный и понятный метод. Конструктор уже содержит логику создания защитных копий (defensive copies) для всех полей.
- Оценка необходимости копирования: Если объект состоит только из примитивов и ссылок на другие иммутабельные объекты (например,
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();
}
}
Вывод: Копирование иммутабельного объекта должно гарантировать, что новая копия так же защищена от внешних изменений, как и оригинал.