Почему метод clone () защищен в java.lang.Object?

112

Какая конкретная причина clone()определяется как защищенная в java.lang.Object?

Алексей Н.
источник

Ответы:

107

Тот факт, что clone защищен, крайне сомнительный, как и тот факт, что cloneметод не объявлен в Cloneableинтерфейсе.

Это делает метод бесполезным для копирования данных, потому что вы не можете сказать :

if(a instanceof Cloneable) {
    copy = ((Cloneable) a).clone();
}

Думаю, что дизайн Cloneableсейчас во многом расценивается как ошибка (цитата ниже). Обычно я хотел бы иметь возможность реализовать интерфейс, Cloneableно не обязательно создавать интерфейсCloneable (аналогично использованию Serializable). Это невозможно сделать без размышлений:

ISomething i = ...
if (i instanceof Cloneable) {
   //DAMN! I Need to know about ISomethingImpl! Unless...
   copy = (ISomething) i.getClass().getMethod("clone").invoke(i);
}

Цитата из книги Джоша Блоха «Эффективная Java» :
«Интерфейс Cloneable был задуман как интерфейс примеси для объектов, чтобы объявлять о том, что они разрешают клонирование. К сожалению, он не служит этой цели ... Это очень нетипичное использование интерфейсов, и его нельзя эмулировать. ... Чтобы реализация интерфейса оказывала какое-либо влияние на класс, он и все его суперклассы должны подчиняться довольно сложному, не имеющему законной силы и в значительной степени недокументированному протоколу "

Oxbow_lakes
источник
2
Если объект не является клонируемым, то clone () объекта вызовет исключение CloneNotSupportedException. Таким образом, вам нужно быть Cloneable, если вы собираетесь вызвать super.clone () (в результате вызывается Object.clone ()). Я не понимаю, как можно сериализовать объект без реализации Serializable.
Стив Куо,
1
«Я думаю, что дизайн Cloneable сейчас в значительной степени считается ошибкой». [необходима ссылка]
Кевин Панко
Извините, я не имел в виду этого. Я просто имел в виду, что «хороший» дизайн заключается в том, чтобы не расширять интерфейс Serializable- решать, реализовывать ли его, зависит от реализации Serializable. Я расширял это Cloneable- это не то, что должен расширять интерфейс, - но реализация интерфейса может быть свободной Cloneable. Проблема в том, что если у вас есть параметр типа интерфейса, вы спрашиваете его, можно ли его клонировать; но тогда вы не сможете его клонировать!
oxbow_lakes
6
@Kevin - эффективная Java-книга Джоша Блоха, стр.45. «Интерфейс Cloneable был задуман как интерфейс примеси для объектов, чтобы объявлять о том, что они разрешают клонирование. К сожалению, он не может служить этой цели»
oxbow_lakes
Также на той же странице: «Это крайне нетипичное использование интерфейсов, не подлежащих эмуляции» и «Чтобы реализация интерфейса оказывала какое-либо влияние на класс, он и все его суперклассы должны подчиняться довольно сложному, неисполнимый и в значительной степени недокументированный протокол "
oxbow_lakes
30

Интерфейс Clonable - это просто маркер, говорящий, что класс может поддерживать клонирование. Метод защищен, потому что вы не должны вызывать его для объекта, вы можете (и должны) переопределить его как общедоступный.

От вс:

В классе Object метод clone () объявлен защищенным. Если все, что вы делаете, это реализуете Cloneable, только подклассы и члены одного пакета смогут вызывать clone () для объекта. Чтобы разрешить любому классу в любом пакете обращаться к методу clone (), вам придется переопределить его и объявить общедоступным, как это делается ниже. (Когда вы переопределяете метод, вы можете сделать его менее частным, но не более частным. Здесь защищенный метод clone () в Object переопределяется как открытый метод.)

Билл К
источник
И это нормально, пока вы не добавите интерфейсы в микс - попробуйте и попытайтесь клонировать неизвестную реализациюSet
oxbow_lakes
@oxbow_lakes: но, возможно, некоторые реализации Set нельзя клонировать
newacct
3
Вы не можете клонировать ничего, что не реализует интерфейс Clonable - это маркер, который говорит: «Этот класс правильно клонируется» - очень похоже на интерфейс Serializable. Между прочим, есть способ клонирования классов с помощью сериализации, который хорошо работает - погуглите что-то вроде "java serialization clone", и вы, вероятно, найдете несколько способов получить глубокую копию вашего объекта.
Bill K,
4
Вы не можете клонировать ничего, что не реализует интерфейс Cloneable, но то, что что-то реализует интерфейс Cloneable, не означает, что вы можете его клонировать.
Майкл Майерс
1
@BuckCherry toString имеет реализацию по умолчанию, если вы ее назовете, произойдет что-то хорошее, и вы вернете строку. equals имеет реализацию по умолчанию (так же, как ==). Клонирование не может иметь реализации по умолчанию. Если вы вызовете clone для объекта, который еще не реализовал его, вы не получите разумного поведения. Клонирование сложно и не может быть выполнено автоматически (некоторые объекты без конструкторов по умолчанию невозможно в общем случае клонировать), поэтому по умолчанию они делают его немного безопаснее. Однако я думаю, что помещать его на Object вообще не было необходимости.
Bill K
7

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

Эндрю Хэйр
источник
но почему это нужно для этого защищать?
Janusz
3
Он защищен, поэтому вы не используете тот, который находится в объекте (в любом случае он просто вызовет исключение). Они хотят, чтобы вы переопределили его в классе, а затем сделали его общедоступным. (ответил также пару раз ниже)
Bill K
1
И бесполезно при рассмотрении интерфейсов в соответствии с моей точкой зрения ниже
oxbow_lakes
должно быть переопределено не очень ясно, тогда он должен был быть абстрактным, а затем не должен быть в классе объектов, я изо всех сил пытаюсь понять, почему это так.
Кумар Абхишек
4

Метод Clone нельзя напрямую использовать ни с одним объектом, поэтому он предназначен для переопределения подклассом.

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

То, как сейчас реализовано клонирование, заставляет задуматься о том, почему вы хотите использовать клон и как вы хотите, чтобы ваш объект был клонирован.

Silfverstrom
источник
2

Он защищен, потому что реализация по умолчанию делает мелкую поэлементную копию всех полей (включая частные), обходя конструктор . Это не то, что объект может быть разработан для обработки в первую очередь (например, он может отслеживать созданные экземпляры объекта в общем списке или что-то подобное).

По той же причине реализация по умолчанию clone()выбросит, если объект, который она вызывает, не реализует Cloneable. Это потенциально небезопасная операция с далеко идущими последствиями, и поэтому автор класса должен явным образом согласиться.

Павел Минаев
источник
На самом деле реализация по умолчанию (в объекте) вызывает исключение в соответствии с документами ...
Билл К.
2
Нет, это не просто бросок. Из своего JavaDoc: «Клон метода для класса Object выполняет определенную операцию клонирования. Во-первых, если класс этого объекта не реализует интерфейс Cloneable, то генерируется исключение CloneNotSupportedException. Обратите внимание, что все массивы считаются реализующими интерфейс Cloneable. В противном случае этот метод создает новый экземпляр класса этого объекта и инициализирует все его поля точно таким же содержимым соответствующих полей этого объекта, как если бы оно было присвоено; содержимое самих полей не клонируется. "
Павел Минаев
2

Из javadoc cloneable.

* By convention, classes that implement this interface (cloneable) should override 
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.

* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface.  Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.

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

Януш
источник
2
Вы не можете вызвать clone для каждого объекта, потому что он защищен!
Павел Минаев
2

ИМХО это так просто:

  • #clone не должен вызываться для неклонируемых объектов, поэтому он не публикуется
  • #cloneдолжен вызываться подклассами ob, Objectкоторые реализуют Cloneable, чтобы получить неглубокую копию правильного класса

Какова правильная область действия методов, которые должны вызываться подклассами, но не другими классами?

Это protected.

Реализация классов, Cloneableконечно же, сделает этот метод общедоступным, чтобы его можно было вызывать из других классов.

Микаэла Маура Эльшнер
источник
0

Метод Clone () имеет внутреннюю проверку «экземпляр Cloneable или нет». Именно так группа разработчиков Java могла подумать, что ограничит неправильное использование метода clone (). Метод clone () защищен, т.е. доступен только для подклассов. Поскольку объект является родительским классом для всех подклассов, метод Clone () может использоваться всеми классами фактически, если у нас нет вышеуказанной проверки «экземпляра Cloneable». Это причина того, что команда Java могла подумать об ограничении ненадлежащего использования clone () с помощью проверки в методе clone () «это экземпляр Cloneable».

Следовательно, любые классы, реализующие cloneable, могут использовать метод clone () класса Object.

Кроме того, поскольку он сделан защищенным, он доступен только тем подклассам, которые реализуют клонируемый интерфейс. Если мы хотим сделать его общедоступным, этот метод должен быть переопределен подклассом с его собственной реализацией.

САРИКА
источник
-2

Да, та же проблема, что и я. Но я решаю это, реализуя этот код

public class Side implements Cloneable {
    public Side clone() {

        Side side = null;
        try {
            side = (Side) super.clone();
        } catch (CloneNotSupportedException e) {
            System.err.println(e);
        }
        return side;
    }
}

Как и раньше, кто-то сказал.

Fyhao
источник
1
CloneNotSupportedException - еще один пример отмеченного исключения, которое должно быть снято (то есть оно должно расширять RuntimeException, а не Exception). Хотя метод clone () в классе Side реализует Cloneable и, следовательно, никогда не будет вызывать CloneNotSupportedException, Side.clone () все же должен перехватить или объявить исключение. Это просто добавляет к методу clone () лишний шум при обработке исключений.
Дерек Махар
-2

Что ж, разработчики sun - всего лишь люди, и они действительно совершили огромную ошибку, реализовав метод клонирования как защищенный, такую ​​же ошибку, как они реализовали нефункционирующий метод клонирования в ArrayList! Так что в целом даже опытные программисты на Java имеют гораздо более глубокое непонимание метода клонирования.

Однако недавно я нашел быстрое и простое решение для копирования любого объекта со всем его содержимым, независимо от того, как он построен и что он содержит, см. Мой ответ здесь: Ошибка при использовании Object.clone ()

отметка
источник
-3

И снова фреймворк Java JDK демонстрирует блестящее мышление:

Клонируемый интерфейс не содержит «public T clone ()»; , потому что он действует больше как атрибут (например, Serializable), который позволяет клонировать экземпляр.

В этом дизайне нет ничего плохого, потому что:

  1. Object.clone () не будет делать то, что вы хотите, с вашим пользовательским классом.

  2. Если у вас есть Myclass, который реализует Cloneable =>, вы перезаписываете clone () с помощью «public MyClass clone ()»

  3. Если у вас есть MyInterface extends Cloneable и некоторые MyClasses, реализующие MyInterface: просто определите «public MyInterface clone ();» в интерфейсе, и каждый метод, использующий объекты MyInterface, сможет их клонировать, независимо от их MyClass-класса.

Виктор
источник
2
если ваш класс унаследован, ваша реализация клона производного класса не будет безопасной, пока реализации базового класса не предоставят безопасные методы клонирования. Кроме того, наличие интерфейса без метода / атрибута является своего рода необычным условием проектирования. Этот интерфейс не заставляет класс реализовывать клонирование.
prap19