Чому метод clone () захищений у java.lang.Object?


Відповіді:


107

Те, що клон захищений, є надзвичайно сумнівним - як і той факт, що 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 Джоша Блоха :
"Інтерфейс, що клонується, був призначений як інтерфейс для змішування об'єктів, які рекламують, що вони дозволяють клонувати. На жаль, це не відповідає цій цілі ... Це вкрай нетипове використання інтерфейсів, а не імітація. ... Для того, щоб реалізація інтерфейсу мала будь-який вплив на клас, він і всі його суперкласи повинні підкорятися досить складному, невиконаному та багато в чому недокументованому протоколу "


2
Якщо об'єкт не є Cloneable, то клон Object () видасть CloneNotSupportedException. Тому вам потрібно бути Cloneable, якщо ви збираєтесь зателефонувати super.clone () (в результаті чого Object.clone () викликається). Я не бачу, як об’єкт можна серіалізувати без реалізації Serializable.
Стів Куо

1
"Я думаю, що дизайн Cloneable зараз багато в чому вважається помилкою". [потрібна цитата]
Кевін Панько

Вибачте - я цього не мав на увазі. Я просто мав на увазі, що "хороший" дизайн - це не робити розширення інтерфейсу Serializable- вирішувати, чи потрібно реалізовувати, залежить від реалізації Serializable. Я поширював це на Cloneableце - це не те, на що повинен поширюватися інтерфейс, - але реалізація інтерфейсу може бути вільною Cloneable. Проблема полягає в тому, що якщо у вас є параметр типу інтерфейсу, ви запитаєте його, чи він може бути клонований; але тоді ви насправді не можете його клонувати!
oxbow_lakes

6
@Kevin - ефективна Java pp45 Джоша Блоха . "Інтерфейс Cloneable був призначений як інтерфейс mixin для об'єктів, які рекламують, що вони дозволяють клонувати. На жаль, він не виконує цю мету"
oxbow_lakes

Також на тій же сторінці: "Це вкрай нетипове використання інтерфейсів, а не імітація" та "Для того, щоб реалізація інтерфейсу мала будь-який вплив на клас, він і всі його суперкласи повинні підкорятися досить складному, невиконаний і значною мірою незадокументований протокол "
oxbow_lakes

30

Інтерфейс Clonable - це лише маркер, який каже, що клас може підтримувати клон. Метод захищений, тому що ви не повинні викликати його на об'єкті, ви можете (і повинні) його замінити як загальнодоступний.

Від сонця:

У класі Object метод clone () оголошується захищеним. Якщо все, що ви робите, це реалізація Cloneable, лише підкласи та члени одного пакету зможуть викликати clone () на об'єкті. Щоб увімкнути будь-який клас у будь-якому пакеті доступ до методу clone (), вам доведеться його перекрити і оголосити загальнодоступним, як це робиться нижче. (Коли ви заміняєте метод, ви можете зробити його менш приватним, але не більш приватним. Тут метод захищеного клонування () в Object переосмислюється як публічний метод.)


Що добре, поки ви не введете інтерфейси в суміш - спробуйте і спробуйте клонувати невідому реалізаціюSet
oxbow_lakes

@oxbow_lakes: але, можливо, деякі реалізації Set не підлягають клонуванню
newacct

3
Ви не можете клонувати що-небудь, що не реалізує інтерфейс Clonable - це маркер, на якому написано "Цей клас правильно піддається клонуванню" - дуже подібний до інтерфейсу Serializable. До речі, є спосіб клонування класів за допомогою серіалізації, який добре працює - google щось на кшталт "клон серіалізації java", і ви, мабуть, знайдете кілька способів отримати глибоку копію вашого об'єкта.
Білл К

4
Ви не можете клонувати все, що не реалізує інтерфейс Cloneable, але тільки те, що щось реалізує інтерфейс Cloneable, не означає, що ви можете його клонувати.
Майкл Майерс

1
@BuckCherry toString має реалізацію за замовчуванням, якщо ви зателефонуєте це, щось станеться добре, і ви отримаєте назад рядок. equals має реалізацію за замовчуванням (те саме, що ==). Клон не може мати реалізацію за замовчуванням. Якщо ви будете викликати клона на об'єкт, який не реалізував його, ви не отримаєте розумної поведінки. Клонування є складним, і його справді неможливо зробити автоматично (деякі об'єкти без конструкторів за замовчуванням неможливо загально клонувати), тому за замовчуванням вони роблять це трохи безпечнішим. Я думаю, що розміщувати його на "Об'єкт" взагалі, можливо, не було б потрібно.
Білл К

7

cloneзахищено, оскільки це щось, що повинно бути відмінено, щоб воно було специфічним для поточного класу. Хоча можна було б створити публічний cloneметод, який би взагалі клонував будь-який об’єкт, це було б не так добре, як метод, написаний спеціально для класу, який цього потребує.


але чому це потрібно захищати?
Януш

3
Він захищений, тому ви не використовуєте його в об'єкті (він все одно просто кине виняток). Вони хочуть, щоб ви перекрили його в класі, тоді ви зробите це загальнодоступним. (відповів також кілька разів нижче)
Білл К

1
І марно, якщо розглядати інтерфейси відповідно до моєї точки нижче
oxbow_lakes

повинно бути відмінено не дуже зрозуміло, тоді воно повинно було бути абстрактним, а потім не повинно бути в класі об’єктів, я дуже намагаюся зрозуміти, чому це так.
Кумар Абхішек

4

Метод Clone не може бути безпосередньо використаний на жодному об'єкті, тому його передбачає переозброєння підкласом.

Звичайно, це може бути загальнодоступним і просто кинути відповідний виняток, коли клонування неможливо, але я думаю, що це було б оманливим.

Спосіб реалізації клона прямо зараз змушує задуматися про те, чому ви хочете використовувати клон, і як ви хочете клонувати ваш об’єкт.


2

Він захищений, оскільки реалізація за замовчуванням виконує неглибоку копіювальну за всіма полями (включаючи приватну), обходячи конструктор . Це не те, що об’єкт може бути призначений для обробки в першу чергу (наприклад, він може відслідковувати створені екземпляри об'єктів у спільному списку чи щось подібне).

З тієї ж причини реалізація за замовчуванням clone()буде кидатися, якщо об'єкт, на який він викликається, не реалізує Cloneable. Це потенційно небезпечна операція з далекосяжними наслідками, і тому автор класу повинен чітко відмовитися.


Насправді реалізація за замовчуванням (у об’єкті) викидає виняток згідно з документами ...
Білл К

2
Ні, це не просто кидати. З його JavaDoc: "Клон методу для класу" Об'єкт "виконує специфічну операцію клонування. По-перше, якщо клас цього об'єкта не реалізує інтерфейс Cloneable, викидається CloneNotSupportedException. Зауважте, що всі масиви вважаються реалізованими інтерфейсом Cloneable. В іншому випадку цей метод створює новий екземпляр класу цього об’єкта та ініціалізує всі його поля точно з вмістом відповідних полів цього об’єкта, як би за призначенням; вміст полів сам по собі не клонований ".
Павло Мінаєв

2

З явадока клонів.

* 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.

Таким чином, ви можете викликати клона на кожному об'єкті, але це дасть вам більшу частину часу не потрібні результати або виняток. Але рекомендується лише в тому випадку, якщо ви реалізуєте cloneable.


2
Ви не можете викликати клону на кожному Об'єкті, оскільки він захищений!
Павло Мінаєв

2

IMHO це так просто, як це:

  • #clone не слід викликати об'єкти, що не підлягають клонуванню, тому вони не оприлюднюються
  • #cloneповинен викликатися підкласами ob, Objectякі реалізують Cloneable, щоб отримати дрібну копію потрібного класу

Яке правильне поле для методів, які слід називати підкласами, а не іншими класами?

Це protected.

Заняття, що реалізують Cloneableкурс, зробить цей метод загальнодоступним, тому його можна буде викликати з інших класів.


0

Метод Clone () має внутрішню перевірку "екземпляра Cloneable чи ні". Саме так, як вважає команда Java, обмежується неналежне використання методу clone () method.clone () захищено, тобто доступ до нього здійснюється лише підкласами. Оскільки об'єкт є батьківським класом усіх підкласів, то метод Clone () може бути використаний усіма класами infact, якщо у нас немає вище перевірки "екземпляра 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;
    }
}

Так само, як раніше хтось сказав.


1
CloneNotSupportedException - це ще один приклад перевіреного винятку, який слід скасувати (тобто він повинен розширити RuntimeException, а не виняток). Хоча метод clone () у класі Side реалізує Cloneable і тому ніколи не кине CloneNotSupportedException, Side.clone () все одно повинен виловлювати або оголошувати виняток. Це просто додає зайвий виняток обробки шуму методу clone ().
Дерек Махар

-2

Ну, а також розробники сонця є лише людьми, і вони дійсно зробили величезну помилку в застосуванні методу клонування як захищеного, та ж помилка, що вони застосували непрацюючий метод клонування в ArrayList! Отже, загалом існує набагато глибше непорозуміння навіть досвідчених програмістів Java щодо методу клонування.

Однак нещодавно я знайшов швидке і просте рішення скопіювати будь-який об’єкт з усім його вмістом, незалежно від того, як він побудований і що він містить, дивіться мою відповідь тут: Помилка використання Object.clone ()


-3

Знову ж таки, рамка Java JDK демонструє геніальне мислення:

Інтерфейс, що піддається клонуванню, не містить "загальнодоступного клона T ();" метод, оскільки він більше схожий на атрибут (наприклад, Serializable), що дозволяє його екземпляру клонувати.

У цьому дизайні немає нічого поганого, оскільки:

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

  2. Якщо у вас Myclass реалізує Cloneable => ви перезаписуєте clone () з "public clone MyClass ()"

  3. Якщо у вас MyInterface розширює Cloneable та деякі MyClasses, що реалізують MyInterface: просто визначте "загальний клон MyInterface ();" в інтерфейсі та кожному методі, що використовує MyInterface, об'єкти зможуть їх клонувати, незалежно від класу MyClass.


2
якщо ваш клас наслідується, ваш похідний клон реалізації класу не буде безпечним, поки ваші реалізації базового класу не забезпечать безпечні методи клонування. Крім того, незвичайним умовою дизайну є наявність інтерфейсу без методу / атрибута. Цей інтерфейс не змушує клас реалізувати клон.
prap19
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.