Дублювання об’єктів

Метод Objectсlone допомагає виробляти в ваших класах дублювання обєктів При дублюванні повертається новий обєкт, початковий стан якого копіює стан обєкта, для якого був викликаний метод clone Усі наступні зміни, що вносяться в обєкт-дубль, не змінюють стану вихідного обєкта

При написанні методу clone слід враховувати три основних моменти:

Для нормальної роботи методу clone необхідно реалізувати інтерфейс Cloneable / У майбутніх реалізаціях назву інтерфейсу може бути виправлено на Clonable /

Метод Objectclone виконує просте дублювання, що полягає в копіюванні всіх полів вихідного обєкта в новий обєкт Для багатьох класів такий варіант працює, але, можливо, у вашому класі його доведеться доповнити за рахунок перевизначення методу (див нижче)

Виняток CloneNotSupportedException сигналізує про те, що метод clone

даного класу не повинен викликатися

Існує чотири варіанти відносини класу до методу clone:

Клас підтримує clone Такі класи реалізують Cloneable, а в оголошенні методу clone зазвичай не вказується ніяких запускаються винятків

Клас умовно підтримує clone Такий клас може являти собою колекцію (набір обєктів), яка в принципі може дублюватися, але лише за умови, що дублюється всі її вміст Такі класи реалізують Cloneable, але при цьому допускають виникнення в методі clone винятку CloneNotSupportedException, яке може бути отримано від інших обєктів при спробі їх дублювання під час дублювання колекції Крім того, буває бажано дозволити можливість дублювання класу, але при цьому не вимагати, щоб дублювання також підтримувалося і для всіх підкласів

Клас дозволяє підтримку clone в підкласах, але не оголошує про це відкрито Такі класи не реалізують Cloneable, але забезпечують реалізацію clone для правильного дублювання полів, якщо реалізація по замовчуванням виявляється неправильною

Клас забороняє clone Такі класи не реалізують Cloneable, а метод clone в них завжди запускає виняток CloneNotSupportedException

Objectclone спочатку перевіряє, чи підтримується інтерфейс Cloneable обєктом, для якого викликаний метод clone якщо ні – запускається виняток CloneNotSupportedException В іншому випадку створюється новий обєкт, тип якого збігається з типом вихідного, і його поля инициализируются значеннями полів вихідного обєкта При завершенні роботи Objectclone повертає посилання на новий обєкт

Найпростіша можливість створити дубльований клас – оголосити про реалізацію в ньому інтерфейсу Cloneable:

public class MyClass extends AnotherClass implements Cloneable

{

// ..

}

Метод clone в інтерфейсі Cloneable має атрибут public, отже, метод MyClassclone, успадкований від Object, також буде public Після такого оголошення можна дублювати обєкти MyClass Дублювання в даному випадку виконується тривіально – Objectclone копіює всі поля MyClass в новий обєкт і повертає його

У методі Cloneableclone присутній оголошення throws CloneNotSupportedException, так що можлива ситуація, при якій клас є дубльованих, а його підклас – ні У підкласі буде реалізований інтерфейс Cloneable (так як він розширює клас, в якому є даний інтерфейс), однак насправді обєкти підкласу не можуть дублюватися Вихід виявляється простим – розширений клас перевизначає метод clone так, щоб останній завжди запускав виняток CloneNotSupportedException, і про це повідомляється в документації Дотримуйтеся обережності – такий підхід означає,

що під час виконання програми не можна визначити, чи допускається дублювання обєктів класу, простою перевіркою реалізації інтерфейсу Cloneable Деякі

класи, для яких дублювання заборонено, змушені сигналізувати про це,

запускаючи виняток

Більшість класів є дубльованими Навіть якщо ви не реалізуєте в своєму класі інтерфейс Cloneable, необхідно переконатися в правильності роботи методу clone У багатьох випадках реалізація, прийнята за замовчуванням, не підходить, оскільки при її виконанні відбувається небажане розмноження посилань на обєкти У таких випадках необхідно перевизначити метод clone і виправити його поведінку За замовчуванням значення кожного поля вихідного обєкта присвоюється аналогічного полю нового обєкта

Якщо, наприклад, у вашому обєкті присутнє посилання на масив, то в дублікаті обєкта також знаходитиметься посилання на той же самий масив Якщо дані масиву доступні тільки для читання, то в подібному спільне використання, швидше за все, немає нічого поганого Однак нерідко потрібна, щоб посилання всередині вихідного обєкта та дубліката були різними, – ймовірно, ситуація, при якій дублікат може змінити вміст масиву у вихідному обєкті або навпаки, виявиться небажаною

Пояснимо суть проблеми на прикладі Припустимо, у нас є простий стек,

містить цілі числа:

public class IntegerStack implements Cloneable {

private int[] buffer

private int top

public IntegerStack(int maxContents) { buffer = new int[maxContents] top = -1

}

public void push(int val) {

buffer[++top] = val

}

Тепер розглянемо фрагмент програми, який створює обєкт Integer Stack, заносить в нього дані і потім дублює:

IntegerStack first = new IntegerStack(2)

firstpush(2)

firstpush(9)

IntegerStack second = (IntegerStack)firstclone()

При використанні методу clone, прийнятого за замовчуванням, дані в памяті будуть виглядати наступним чином:

Тепер розглянемо, що відбудеться після виклику firstpop (), за яким слідує

firstpush (17) Значення верхнього елементу стека first, як і очікувалося, змінюється з 9 на

17 Проте, на подив програміста, верхній елемент стека second теж стає рівним 17, оскільки обидва стека спільно використовують один і той же масив даних

Вихід полягає в перевизначенні методу clone і створення в ньому окремим копії масиву:

public Object clone() {

try {

IntegerStack nObj = (IntegerStack)superclone()

nObjbuffer = (int[])bufferclone()

return nObj

} catch (CloneNotSupportedExeption e) {

/ / Не може статися – метод clone () підтримується

/ / Як нашим класом, так і масивами

throw new InternalError(etoString())

}

}

Метод clone починається з виклику superclone, присутність якого надзвичайно важливо, оскільки суперклас може вирішувати якісь свої проблеми, повязані з спільно використовуваними обєктами Якщо не викликати метод суперкласу, то це вирішить одні проблеми, створивши натомість інші Крім того, виклик superclone призводить до обігу до методу Objectclone, що створює обєкт правильного типу Побудова обєкта IntegerStack в IntegerStackclone призведе до неправильної роботи класів-розширень IntegerStack – при виклику superclone в розширеному класі буде створюватися обєкт типу IntegerStack, а не потрібного, розширеного типу

Потім значення, що повертається superclone, перетвориться в посилання на IntegerStack Механізм приведення типів описаний в розділі Приведення типів; з його допомогою посилання на один тип (в нашому випадку – тип Object, повертаний clone) перетворюється на заслання на другий тип (IntegerStack) Перетворення виявляється успішним лише в тому випадку, якщо обєкт може використовуватися в якості обєкта типу, до якого перетворюється посилання

Objectclone инициализирует кожне поле обєкта-дубліката, привласнюючи йому значення відповідного поля вихідного обєкта Вам залишається тільки написати спеціалізований код для обробки тих полів, для яких копіювання за значенням не підходить IntegerStackclone не копіює поле top, оскільки в нього вже занесено правильне значення під час стандартного копіювання значень полів

Після появи спеціалізованого методу clone вміст памяті для нашого прикладу буде виглядати так:

Іноді домогтися правильної роботи clone виявляється настільки складно, що гра не варта свічок, і деякі класи відмовляються від підтримки clone У таких випадках ваш метод clone повинен порушувати виняток Clone NotSupportedException, щоб його виклик ніколи не приводив до створення неправильних обєктів

Ви також можете вимагати, щоб метод clone підтримувався у всіх підкласах даного класу, – для цього слід перевизначити метод clone так, щоб у його сигнатуру не входило оголошення про порушення виключення CloneNotSupportedException В результаті підкласи, в яких реалізується метод clone, не зможуть порушувати виняток CloneNotSupported Exception, оскільки методи підкласу не можуть запроваджувати нові винятки Аналогічно, якщо метод clone у вашому класі оголошений public, то у всіх розширених класах він також буде мати атрибут public – згадаємо про те, що метод підкласу не може бути менше доступним, ніж відповідний йому метод суперкласу

Вправа 38

Реалізуйте інтерфейс Cloneable в класах Vehicle і PassengerVehicle Який з чотирьох описаних вище варіантів ставлення до дублювання має бути реалізований в кожному з цих класів Чи спрацює просте копіювання, здійснюване в Objectclone, при дублюванні обєктів цих класів

Вправа 39

Напишіть клас Garage (гараж), обєкти якого будуть зберігати в масиві деяке число обєктів Vehicle Реалізуйте інтерфейс Cloneable і напишіть для нього відповідний метод clone Напишіть метод Garagemain для його тестування

Вправа 310

Реалізуйте в класі LinkedList інтерфейс Cloneable метод clone повинен повертати

новий список зі значеннями вихідного списку (а не з їх дублікатами) Іншими словами,

зміни в одному списку не повинні зачіпати другого списку, проте зміни в обєктах, посилання на які зберігаються в списку, повинні виявлятися в обох списках

Джерело: Арнольд К, Гослінг Д – Мова програмування Java (1997)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*