Я мог бы попытаться ответить на этот вопрос, но я бы просто занялся плагиатом Джоша Блоха, так что вместо этого вот ссылка на ресурс:
Билл Веннерс: В своей книге вы рекомендуете использовать конструктор копирования вместо реализации Cloneable
и написания clone
. Не могли бы вы уточнить это?
Джош Блох: Если вы читали статью о клонировании в моей книге, особенно если вы читаете между строк, вы поймете, что я думаю, что клон глубоко сломан. Есть несколько недостатков дизайна, самый большой из которых заключается в том, что интерфейс Cloneable
не имеет метода clone
. А это значит, что это просто не работает: создание чего-либо Cloneable
ничего не говорит о том, что вы можете с ним сделать. Вместо этого он говорит что-то о том, что он может сделать внутри. В нем говорится, что если при повторном вызове super.clone
он вызывает метод Object
clone
, этот метод возвращает копию поля оригинала.
Но это ничего не говорит о том, что вы можете сделать с объектом, который реализует интерфейс Cloneable
, что означает, что вы не можете выполнить полиморфную операцию clone
. Если бы у меня был массив Cloneable
, вы бы подумали, что я могу запустить этот массив и клонировать каждый элемент, чтобы сделать глубокую копию массива, но я не могу. Вы не можете привести что-либо к Cloneable
и вызвать метод clone
, потому что Cloneable
не имеет общедоступного метода clone
и не имеет Object
. Если вы попытаетесь привести к Cloneable
и вызвать метод clone
, компилятор скажет, что вы пытаетесь вызвать защищенный метод clone
для объекта.
Правда в том, что вы не предоставляете своим клиентам никаких возможностей, внедряя Cloneable
и предоставляя общедоступный метод clone
, отличный от возможности копирования. Это не лучше, чем у вас, если вы предоставляете операцию копирования с другим именем и не используете Cloneable
. Это в основном то, что вы делаете с конструктором копирования. Подход конструктора копирования имеет несколько преимуществ, которые я обсуждаю в книге. Одним из больших преимуществ является то, что копия может быть сделана, чтобы иметь представление, отличное от оригинала. Например, вы можете скопировать LinkedList
в ArrayList
.
Метод
Object
* clone
очень сложный. Он основан на полевых копиях и является «внеязыковым». Он создает объект без вызова конструктора. Нет никаких гарантий, что он сохранит инварианты, установленные конструкторами. За эти годы было много ошибок, как внутри, так и за пределами Sun. Это связано с тем, что если вы просто несколько раз вызываете super.clone
вверх по цепочке, пока не клонируете объект, у вас будет мелкая копия объекта. Клон обычно разделяет состояние с клонируемым объектом. Если это состояние изменчиво, у вас нет двух независимых объектов. Если вы измените один, другой тоже изменится. И вдруг вы получаете случайное поведение.
Есть очень мало вещей, для которых я больше использую Cloneable
. Я часто предоставляю публичный метод clone
для конкретных классов, потому что люди ожидают этого. У меня нет абстрактных классов, реализующих Cloneable
, и нет интерфейсов, расширяющих его, потому что я не возьму бремя реализации Cloneable
на все классы, которые расширяют (или реализуют) абстрактный класс (или интерфейс) , Это настоящее бремя, с небольшими преимуществами.
Даг Ли идет еще дальше. Он сказал мне, что он больше не использует clone
, кроме как для копирования массивов. Вы должны использовать clone
для копирования массивов, потому что, как правило, это самый быстрый способ сделать это. Но типы Дуга просто не реализуют Cloneable
больше. Он разочаровался в этом. И я думаю, что это не лишено смысла.
Обидно, что Cloneable
сломан, но это случается. Оригинальные API Java были сделаны очень быстро в сжатые сроки, чтобы соответствовать закрывающемуся окну рынка. Оригинальная команда Java проделала невероятную работу, но не все API идеальны. Cloneable
- это слабое место, и я думаю, что люди должны знать о его ограничениях.