Защитные копии Java
Я видел защитные копии, закодированные так
void someMethod(Date d) {
myDate = new Date( d.getTime() );
}
Но это не имеет смысла для меня, разве в Java нет способа создать идентичную копию в памяти этого объекта?
Я прочитал clone()
не будет работать во всех случаях, но я не понимаю, почему.
4 ответа
Я мог бы попытаться ответить на это, но я бы просто занялся плагиатом Джоша Блоха, так что вместо этого вот ссылка на ресурс:
Копировать конструктор против клонирования
Билл Веннерс: в своей книге вы рекомендуете использовать конструктор копирования вместо реализации
Cloneable
и писатьclone
, Не могли бы вы уточнить это?Джош Блох: Если вы читали статью о клонировании в моей книге, особенно если вы читаете между строк, вы поймете, что я думаю, что клон глубоко сломан. Есть несколько недостатков дизайна, самый большой из которых заключается в том, что
Cloneable
Интерфейс не имеетclone
метод. А это значит, что это просто не работает: сделать что-тоCloneable
ничего не говорит о том, что вы можете сделать с этим. Вместо этого он говорит что-то о том, что он может сделать внутри. Он говорит, что если по телефонуsuper.clone
неоднократно это заканчивает тем, что звонилоObject
"sclone
метод, этот метод будет возвращать копию поля оригинала.Но это ничего не говорит о том, что вы можете сделать с объектом, который реализует
Cloneable
интерфейс, что означает, что вы не можете сделать полиморфныйclone
операция. Если у меня есть массивCloneable
Вы могли бы подумать, что я мог бы запустить этот массив и клонировать каждый элемент, чтобы сделать глубокую копию массива, но я не могу. Вы не можете бросить что-тоCloneable
и позвонитьclone
метод, потому чтоCloneable
не имеет общественностиclone
метод и не делаетObject
, Если вы попытаетесь привести кCloneable
и позвонитьclone
метод, компилятор скажет, что вы пытаетесь вызвать защищенныйclone
метод на объекте.Дело в том, что вы не предоставляете своим клиентам никаких возможностей, внедряя
Cloneable
и предоставление общественностиclone
метод, кроме способности копировать. Это не лучше, чем вы получаете, если вы предоставляете операцию копирования с другим именем и не реализуетеCloneable
, Это в основном то, что вы делаете с конструктором копирования. Подход конструктора копирования имеет несколько преимуществ, которые я обсуждаю в книге. Одним из больших преимуществ является то, что копия может быть сделана так, чтобы иметь представление, отличное от оригинала. Например, вы можете скопироватьLinkedList
вArrayList
,
Object
"sclone
метод очень хитрый. Он основан на полевых копиях и является "внеязыковым". Он создает объект без вызова конструктора. Нет никаких гарантий, что он сохранит инварианты, установленные конструкторами. За эти годы было много ошибок, как внутри, так и за пределами Sun, что связано с тем, что если вы просто позвонитеsuper.clone
несколько раз вверх по цепочке, пока вы не клонируете объект, у вас есть мелкая копия объекта. Клон обычно разделяет состояние с клонируемым объектом. Если это состояние изменчиво, у вас нет двух независимых объектов. Если вы измените один, другой тоже изменится. И вдруг, вы получаете случайное поведение.Есть очень мало вещей, для которых я использую
Cloneable
больше. Я часто предоставляю публикеclone
метод на конкретных классах, потому что люди ожидают этого. У меня нет абстрактных классов реализоватьCloneable
и у меня нет интерфейсов, расширяющих его, потому что я не возьму на себя бремя реализацииCloneable
на всех классах, которые расширяют (или реализуют) абстрактный класс (или интерфейс). Это настоящее бремя, с небольшими преимуществами.Даг Ли идет еще дальше. Он сказал мне, что он не использует
clone
больше, кроме как для копирования массивов. Вы должны использоватьclone
копировать массивы, потому что это, как правило, самый быстрый способ сделать это. Но типы Дуга просто не реализуютCloneable
больше. Он разочаровался в этом. И я думаю, что это не лишено смысла.Обидно что
Cloneable
сломан, но это случается. Оригинальные API Java были сделаны очень быстро в сжатые сроки, чтобы соответствовать закрывающемуся окну рынка. Оригинальная команда Java проделала невероятную работу, но не все API идеальны.Cloneable
это слабое место, и я думаю, что люди должны знать о его ограничениях.
clone()
реализуется осмысленно только в некоторых классах, потому что существует множество решений, связанных с клонированием, которые JVM или компилятор не принимает за вас (например, мелкое копирование или глубокое копирование). Некоторые также утверждают, что вся концепция Java нарушена и поэтому используется редко.
Конечным результатом, однако, является то, что многие классы не могут быть клонированы. В этих случаях пользователь клонирует их, генерируя и инициализируя один объект на основе другого.
Тем не менее Date
реализует класс Cloneable
так что в данном конкретном случае нет никакой пользы от использования "конструктора копирования".
Одна из ситуаций, когда клонирование предпочтительнее, это когда вы работаете с иерархиями классов. Представьте, что вы представляете математическое выражение в виде дерева подтипов выражения, где вы ссылаетесь на самое внешнее выражение.
Допустим, в данном случае это плюс. Вы бы назвали clone ссылкой на Expression, но на самом деле вы получите новый экземпляр Plus. С конструкторами вы не можете этого сделать, потому что ваше выражение может быть интерфейсом или абстрактным классом.
Нет простого способа сделать идентичную копию, которая всегда работает.
Клон должен был это сделать, но он реализован не всеми классами и, таким образом, более или менее нарушен.
Другой подход заключается в сериализации / десериализации, но сериализация также поддерживается не всеми классами.
С точки зрения безопасности мобильного кода, clone
обычно может быть переопределено.
@Override public Date clone() {
return this; // Ha!
}
Даже если вы не заботитесь о безопасности, вы можете представить, что программист "умный", вызывая сообщение об ошибке в вашем коде.
Ожидание, что особенно clone
вернет точно такой же тип во время выполнения и вызовет проблемы с реализацией.