Метод clone и интерфейс Cloneable в Java
Метод clone() класса Object
protected native Object clone() throws CloneNotSupportedException;
Интерфейс Cloneable
public interface Cloneable {}
Общий контракт для метода clone
Метод clone создает и возвращает копию этого объекта. Точное значение слова "копия" может зависеть от класса объекта. Общее намерение состоит в том, чтобы для любого объекта x выражение
x.clone() != x
будет true, и выражение
x.clone().getClass() == x.getClass()
будет true, но это не абсолютные требования. Хотя обычно
x.clone().equals(x)
будет true, это не абсолютное требование. Копирование объекта обычно влечет за собой создание нового экземпляра его класса, но также может потребоваться копирование внутренних структур данных. Никакие конструкторы не вызываются.
- Если вы переопределите метод clone в нефинальном классе, вы должны вернуть объект, полученный с помощью вызова super.clone()
- Методы super.clone() автоматически копируют примитивные значения
- На практике ожидается, что класс, реализующий Cloneable, предоставит правильно работающий метод public clone().
- метод clone() функционирует как другой конструктор; вы должны убедиться, что он не причиняет вреда исходному объекту и правильно устанавливает инварианты для клона.
Примеры реализации метода clone()
public class Stack {
private Object[] elements;
private int size = 0;
@Override public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elements = elements.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
public class HashTable implements Cloneable {
private Entry[] buckets = ...;
private static class Entry {
final Object key;
Object value;
Entry next;
Entry(Object key, Object value, Entry next) {
this.key = key;
this.value = value;
this.next = next;
}
// Рекурсивно копировать связанный список,
// возглавляемый этой Entry
Entry deepCopy() {
return new Entry(key, value, next == null ? null : next.deepCopy());
}
// Итеративное копирование связанного списка,
// возглавляемого этой Entry
Entry deepCopy() {
Entry result = new Entry(key, value, next);
for (Entry p = result; p.next != null; p = p.next) {
p.next = new Entry(p.next.key, p.next.value, p.next.next);
}
return result;
}
}
@Override public HashTable clone() {
try {
HashTable result = (HashTable) super.clone();
result.buckets = new Entry[buckets.length];
for (int i = 0; i < buckets.length; i++) {
if (buckets[i] != null) {
result.buckets[i] = buckets[i].deepCopy();
}
}
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
Заключения:
- Все классы, реализующие Cloneable, должны переопределять clone
- Метод clone() должен сначала вызвать super.clone, а затем исправить все поля, которые необходимо исправить.
- Не создавайте интерфейс расширяющий Cloneable
- Не реализуйте Cloneable для любого класса, предназначенного для наследования
Альтернативы использования clone():
- Конструктор копирования
public Example(Example example);
- Фабрика копирования
public static Example newInstance(Example example);
Читайте также:
- Интерфейсы Comparable и Comparator в Java
- Разница между fail-fast и fail-safe итератором в Java
- Решение проблемы производителя-потребителя с помощью BlockingQueue в Java
Комментарии
Отправить комментарий