О Java cloneable

Я искал несколько руководств, объясняющих о Java Cloneable, но не получал никаких хороших ссылок, и Qaru становится все более очевидным выбором.

Я хотел бы знать следующее:

  • Cloneable означает, что мы можем иметь клон или копию объектов, посредством реализуя интерфейс Cloneable. Каковы преимущества и недостатки этого?
  • Как происходит рекурсивное клонирование, если объект является композитный объект?

Спасибо.

Ответ 1

Первое, что вы должны знать о Cloneable, - это не использовать.

Очень сложно реализовать клонирование с помощью Cloneable вправо, и это не стоит того.

Вместо этого используйте некоторые другие параметры, такие как apache-commons SerializationUtils (глубокий клон) или BeanUtils (неглубокий клон) или просто используйте конструктор-копию.

Смотрите здесь для взглядов Джоша Блоха о клонировании с помощью Cloneable, что объясняет многие недостатки подхода. (Джошуа Блох был сотрудником Sun и руководил разработкой многочисленных функций Java.)

Ответ 2

Клонируемый сам, к сожалению, просто интерфейс-маркер, то есть: он не определяет метод clone().

Что происходит, это изменение поведения защищенного метода Object.clone(), который будет генерировать исключение CloneNotSupportedException для классов, которые не реализуют Cloneable, и выполнять неполную копию списка для классов, которые делают.

Даже если это поведение, которое вы ищете, вам все равно нужно реализовать свой собственный метод clone(), чтобы сделать его общедоступным.

При реализации вашего собственного clone() идея состоит в том, чтобы начать с создания объекта super.clone(), который, как гарантируется, будет иметь правильный класс, а затем сделать любую дополнительную популяцию полей в случае мелкой копии это не то, что вы хотите. Вызов конструктора из clone() будет проблематичным, так как это приведет к разрыву наследования в случае, если подкласс хочет добавить свою собственную дополнительную клонированную логику; если бы он вызывал super.clone(), в этом случае он получил бы объект неправильного класса.

Этот подход обходит любую логику, которая может быть определена в ваших конструкторах, хотя это может быть проблематично.

Другая проблема заключается в том, что любые подклассы, которые забывают переопределить clone(), автоматически наследуют мелкую копию по умолчанию, которая, скорее всего, не является тем, что вы хотите в случае изменчивого состояния (которое теперь будет разделяться между источником и копией).

Большинство разработчиков не используют Cloneable по этим причинам и вместо этого просто реализуют конструктор копирования.

Для получения дополнительной информации и потенциальных ловушек Cloneable я настоятельно рекомендую книгу "Эффективная Java" Джошуа Блоха

Ответ 3

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

Итак, используйте Cloneable разумно. Это не дает вам достаточных преимуществ по сравнению с усилиями, которые вам нужно применить, чтобы все было правильно.

Ответ 4

Клонирование - это базовая парадигма программирования. Тот факт, что Java, возможно, реализовал его слабо во многом, вовсе не уменьшает необходимость клонирования. И, легко реализовать клонирование, которое будет работать, но вы хотите, чтобы он работал, мелкий, глубокий, смешанный, что угодно. Вы даже можете использовать клонирование имени для функции и не применять Cloneable, если хотите.

Предположим, что у меня есть классы A, B и C, где B и C получены из A. Если у меня есть список объектов типа A, как это:

ArrayList<A> list1;

Теперь этот список может содержать объекты типа A, B или C. Вы не знаете, какой тип объектов. Таким образом, вы не можете скопировать список следующим образом:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    list2.add(new A(a));
}

Если объект действительно имеет тип B или C, вы не получите правильную копию. И что, если А абстрактно? Теперь некоторые люди предложили это:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    if(a instanceof A) {
        list2.add(new A(a));
    } else if(a instanceof B) {
        list2.add(new B(a));
    } else if(a instanceof C) {
        list2.add(new C(a));
    }
}

Это очень и очень плохая идея. Что делать, если вы добавляете новый производный тип? Что делать, если B или C находятся в другом пакете, и у вас нет доступа к ним в этом классе?

Что вы хотели бы сделать, так это:

ArrayList<A> list2 = new ArrayList<A>();
for(A a : list1) {
    list2.add(a.clone());
}

Многие люди указали, почему основная реализация Java-клона проблематична. Но он легко преодолевает этот путь:

В классе A:

public A clone() {
    return new A(this);
}

В классе B:

@Override
public B clone() {
    return new B(this);
}

В классе C:

@Override
public C clone() {
    return new C(this):
}

Я не реализую Cloneable, просто используя одно и то же имя функции. Если вам это не нравится, назовите его чем-то другим.

Ответ 5

A) Существует не так много преимуществ клонирования над конструктором копии. Вероятно, самой большой из них - возможность создания нового объекта того же самого динамического типа (при условии, что объявленный тип клонируется и имеет общедоступный метод клонирования).

B) Клон по умолчанию создает мелкую копию, и он останется мелкой копией, если ваша реализация клона не изменится. Это может быть сложно, особенно если ваш класс имеет окончательные поля

Божо прав, клонировать может быть трудно. Конструктор копирования / factory будет удовлетворять большинство потребностей.

Ответ 6

Каковы недостатки Cloneable?

Клонирование очень опасно, если объект, который вы копируете, имеет состав. В этом случае вам нужно подумать о возможном побочном эффекте, потому что клон создает мелкую копию:

Скажем, у вас есть один объект для обработки связанных с db манипуляций. Скажем, этот объект имеет объект Connection как одно из свойств.

Итак, когда кто-то создает клон originalObject, создаваемый объект, скажем, cloneObject. Здесь originalObject и cloneObject сохраняют одну и ту же ссылку для объекта Connection.

Скажем, originalObject закрывает объект Connection, поэтому теперь cloneObject не будет работать, потому что объект Connection был разделен между ними, и он был актуален закрыт originalObject.

Аналогичная проблема может возникнуть, если вы хотите клонировать объект, у которого IOStream является свойством.

Как происходит рекурсивное клонирование, если объект является составным объектом?

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