Автор: Widowmaker1984

Вопрос 92 (про клонирование объектов)

"Почему метод clone объявлен как protected? Что необходимо для реализации клонирования?"

1. Иногда необходимо получить копию объекта, которая не зависела бы от оригинала и с которой можно было бы производить манипуляции, при этом не изменяя оригинал. При обыкновенном присваивание объектов (obj1 = obj2;) передаются ссылки на объект. В итоге два экземпляра ссылаются на один объект, и изменение одного приведет к изменению другого. В данном случае, на помощь придет интерфейс Cloneable и метод clone() класса Object.

1.1. Метод clone() класса Object создает и возвращает копию объекта с такими же значениями полей. Если копируемый объект содержит ссылки на другие объекты, то ссылки будут скопированы, но дубликаты тех объектов не создаются. Это называется "поверхностным" (shallow) клонированием. Оно работает корректно (по соглашению, метод clone() должен возвращать объект независимый от клонируемого объекта), если объект содержит только примитивные поля или ссылки на неизменяемые объекты.

1.2. Обратите внимание, что метод clone нативный и делает нечто, что иными способами сделать нельзя, а именно создает копию объекта без вызова конструктора.

2. Метод clone() может выбрасывать исключение CloneNotSupportedException. Данное исключение возникает в случае, когда клонируемый класс не имеет реализации интерфейса Cloneable. Интерфейс Cloneable не реализует ни одного метода. Он является всего лишь маркером, говорящим, что данный класс реализует клонирование объекта.

2.1. Сам Object не реализует Clonable, а метод объявлен, как protected. Поэтому он доступен только при наследовании от объекта. Но это не является проблемой, потому как любой класс, является потомком класса Object. При переопределении метод clone() необходимо расширить до public.

2.2.1. Пример расширения метода clone().
public User clone() throws CloneNotSupportedException {
return (User)super.clone();
}

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

3. Перед тем как объявлять свой класс как Cloneable, следует внимательно проанализировать его структуру, и определить какие поля нужно проинициализировать после выполнения super.clone().

3.1. Из-за того, что интерфейс Cloneable не имеет публичного метода clone(), к этой операции не получается применить полиморфизм в том смысле, что для массива типа Cloneable нельзя вызвать данный метод. Поэтому по сути использование данного интерфейса ничем не лучше, чем предоставление классу аналогичной функциональности через метод с другим именем и без использования данного интерфейса.

3.2. Пример альтернативного решения — использование конструктора копирования, принимающего как параметр объект копируемого типа. Одно из преимуществ конструктора: возможность сменить тип представления данных (например, скопировать данные из LinkedList в ArrayList).

3.3. Но оба вышеуказанных способа полны потенциальных ошибок. Наиболее удобным и гибким способом клонирования является механизм сериализации. Он заключается в сохранении объекта в поток байтов с последующим восстановлением. Если нет особой необходимости обработки полей во время клонирования объектов, именно сериализация является наиболее предпочтительным вариантом для этих целей.

3.3.1. Пример:
Cat vaska = new Cat("Vaska","Gray",4);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream ous = new ObjectOutputStream(baos);
//сохраняем состояние кота Васьки в поток и закрываем его(поток)
ous.writeObject(vaska);
ous.close();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
//создаём кота для опытов и инициализируем его состояние Васькиным
Cat cloneVaska = (Cat)ois.readObject();

Комментарии


Лучшее   Правила сайта   Вход   Регистрация   Восстановление пароля

Материалы сайта предназначены для лиц старше 16 лет (16+)