Защитные копии Java - PullRequest
       18

Защитные копии Java

1 голос
/ 10 февраля 2010

Я видел защитные копии, закодированные так:

void someMethod(Date d) {
    myDate = new Date( d.getTime() );
}

Но это не имеет смысла для меня, разве в Java нет способа создать идентичную копию в памяти этого объекта?

Я прочитал clone() не будет работать во всех случаях, но я не понимаю, почему.

Ответы [ 4 ]

7 голосов
/ 10 февраля 2010

Я мог бы попытаться ответить на этот вопрос, но я бы просто занялся плагиатом Джоша Блоха, так что вместо этого вот ссылка на ресурс:

Конструктор копирования и клонирование

Билл Веннерс: В своей книге вы рекомендуете использовать конструктор копирования вместо реализации 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 - это слабое место, и я думаю, что люди должны знать о его ограничениях.

3 голосов
/ 10 февраля 2010

clone() осмысленно реализовано только в некоторых классах, потому что существует множество решений, связанных с клонированием, которые JVM или компилятор не принимает за вас (например, поверхностное или глубокое копирование). Некоторые также утверждают, что вся концепция Java нарушена и поэтому используется редко.

Однако конечным результатом является то, что многие классы не могут быть клонированы. В этих случаях пользователь клонирует их, генерируя и инициализируя один объект на основе другого.

Однако класс Date реализует Cloneable, поэтому в данном конкретном случае нет особой выгоды по сравнению с использованием только «конструктора копирования».

Одна из ситуаций, когда клонирование предпочтительнее, - это когда вы работаете с иерархиями классов. Представьте, что вы представляете математическое выражение в виде дерева подтипов выражения, где вы ссылаетесь на самое внешнее выражение.

Допустим, в данном случае это плюс. Вы бы назвали clone ссылкой на Expression, но на самом деле вы получите новый экземпляр Plus. С конструкторами вы не можете этого сделать, потому что ваше выражение может быть интерфейсом или абстрактным классом.

1 голос
/ 10 февраля 2010

Нет простого способа сделать идентичную копию, которая всегда работает.

Клон должен был это сделать, но он реализован не всеми классами и, следовательно, более или менее нарушен.

Другой подход заключается в сериализации / десериализации, но сериализация также поддерживается не всеми классами.

0 голосов
/ 10 февраля 2010

С точки зрения безопасности мобильного кода, clone обычно может быть переопределено.

@Override public Date clone() {
    return this; // Ha!
}

Даже если вы не заботитесь о безопасности, вы можете представить, что программист «умен», вызывая сообщение об ошибке в вашем коде.

Ожидание того, что, в частности, clone вернет точно такой же тип среды выполнения, также вызывает проблемы с реализацией.

...