Мне нужно реализовать глубокий клон в одном из моих объектов, у которого нет суперкласса.
Как лучше всего справиться с проверкой, CloneNotSupportedException
выданной суперклассом (а это есть Object
)?
Коллега посоветовал мне поступить следующим образом:
@Override
public MyObject clone()
{
MyObject foo;
try
{
foo = (MyObject) super.clone();
}
catch (CloneNotSupportedException e)
{
throw new Error();
}
// Deep clone member fields here
return foo;
}
Мне это кажется хорошим решением, но я хотел передать его сообществу StackOverflow, чтобы узнать, есть ли другие идеи, которые я могу включить. Спасибо!
Cloneable
тогда бросаниеAssertionError
простого, а не простогоError
будет немного более выразительным.Ответы:
Вы обязательно должны использовать
clone
? Большинство людей согласны с тем, что Java неclone
работает.Джош Блох о дизайне - Конструктор копирования против клонирования
Вы можете прочитать более подробное обсуждение этой темы в его книге « Эффективное Java 2-е издание», пункт 11:
clone
разумное переопределение . Он рекомендует вместо этого использовать конструктор копирования или фабрику копирования.Он продолжал писать страницы о том, как, если вы чувствуете, что вы должны, вы должны реализовать
clone
. Но он закончил этим:Акцент был сделан на его, а не на моем.
Поскольку вы дали понять, что у вас нет другого выбора, кроме как реализовать
clone
, вот что вы можете сделать в этом случае: убедитесь в этомMyObject extends java.lang.Object implements java.lang.Cloneable
. Если это так, то вы можете гарантировать, что НИКОГДА не поймаетеCloneNotSupportedException
. Бросок,AssertionError
как некоторые предлагали, кажется разумным, но вы также можете добавить комментарий, объясняющий, почему блок catch никогда не будет введен в этом конкретном случае .В качестве альтернативы, как предлагали другие, вы, возможно, можете реализовать
clone
без вызоваsuper.clone
.источник
super.clone()
внутри своих методов клонирования, подкласс, как правило, должен переопределить, толькоclone()
если он добавляет новые поля, содержимое которых необходимо клонировать. Если какой-либо суперкласс используетnew
вместоsuper.clone()
, то все подклассы должны переопределитьclone()
, добавляют ли они какие-либо новые поля.Иногда проще реализовать конструктор копирования:
Это избавляет вас от проблем с обработкой
CloneNotSupportedException
, работает сfinal
полями и вам не нужно беспокоиться о возвращаемом типе.источник
Ваш код работает довольно близко к "каноническому" способу его написания.
AssertionError
Хотя я бы бросил в ловушку. Это сигнализирует, что эта линия никогда не должна достигаться.источник
Cloneable
в целом это неверная идея, как объясняется в Эффективной Java. OP уже выразил, что они должны использоватьCloneable
. Поэтому я понятия не имею, как еще можно улучшить свой ответ, кроме, возможно, его полного удаления.Есть два случая, в которых
CloneNotSupportedException
будет брошено:Cloneable
Клонируемый класс не реализован (при условии, что фактическое клонирование в конечном итоге относится кObject
методу clone). Если класс, в котором вы пишете этот метод, является инструментамиCloneable
, этого никогда не произойдет (поскольку любые подклассы унаследуют его соответствующим образом).Cloneable
.Последний случай не может произойти в вашем классе (поскольку вы напрямую вызываете метод суперкласса в
try
блоке, даже если вызывается из вызова подклассаsuper.clone()
), а первый не должен, поскольку ваш класс явно должен реализоватьCloneable
.По сути, вы должны обязательно зарегистрировать ошибку, но в этом конкретном случае это произойдет только в том случае, если вы испортите определение своего класса. Таким образом, относитесь к нему как к проверенной версии
NullPointerException
(или подобной) - она никогда не будет выбрана, если ваш код работает.В других ситуациях вам нужно быть готовым к такой возможности - нет гарантии, что данный объект можно клонировать, поэтому при перехвате исключения вы должны предпринять соответствующие действия в зависимости от этого условия (продолжить с существующим объектом, использовать альтернативную стратегию клонирования например, сериализовать-десериализовать, бросить,
IllegalParameterException
если вашему методу требуется параметр по клонированию и т. д. и т. д.).Изменить : хотя в целом я должен отметить, что да,
clone()
действительно сложно реализовать правильно, и вызывающим абонентам сложно узнать, будет ли возвращаемое значение тем, что они хотят, вдвойне, если вы рассматриваете глубокие и мелкие клоны. Часто лучше полностью избегать всего этого и использовать другой механизм.источник
super.clone()
.Используйте сериализацию для создания глубоких копий. Это не самое быстрое решение, но оно не зависит от типа.
источник
Вы можете реализовать конструкторы защищенного копирования следующим образом:
источник
clone()
объект, возвращаемый,getMySecondMember()
если у него нетpublic clone
метода.Поскольку большинство ответов здесь верны, я должен сказать, что ваше решение также соответствует тому, как это делают настоящие разработчики Java API. (Либо Джош Блох, либо Нил Гафтер)
Вот выдержка из openJDK, класс ArrayList:
Как вы заметили, и другие упоминали,
CloneNotSupportedException
почти не будет шансов быть брошенным, если вы заявили, что реализуетеCloneable
интерфейс.Кроме того, вам не нужно переопределять метод, если вы не делаете ничего нового в переопределенном методе. Вам нужно только переопределить его, когда вам нужно выполнить дополнительные операции с объектом или вам нужно сделать его общедоступным.
В конечном счете, все же лучше избегать этого и делать это другим способом.
источник
источник
Тот факт, что Java-реализация Cloneable не работает, не означает, что вы не можете создать свою собственную.
Если реальной целью OP было создание глубокого клона, я думаю, что можно создать такой интерфейс:
затем используйте конструктор прототипа, упомянутый ранее, чтобы реализовать его:
и еще один класс с полем объекта AClass:
Таким образом, вы можете легко и глубоко клонировать объект класса BClass без необходимости использования @SuppressWarnings или другого хитроумного кода.
источник