Jump to content

клон (метод Java)

clone() — это метод языка программирования Java для дублирования объектов . В Java объектами манипулируют посредством ссылочных переменных, и нет оператора копирования объекта — оператор присваивания дублирует ссылку, а не объект. Метод clone() обеспечивает эту недостающую функциональность.

Классы, которым нужна функциональность копирования, должны реализовать для этого какой-то метод. В определенной степени эту функцию обеспечивает " Object.clone()".

clone() действует как конструктор копирования. Обычно он вызывает clone() метод своего суперкласса для получения копии и т. д., пока она в конечном итоге не достигнет Object's clone() метод. Специальный clone() метод в базовом классе Object предоставляет стандартный механизм дублирования объектов.

Класс Object's clone() метод создает и возвращает копию объекта с тем же классом и со всеми полями, имеющими одинаковые значения. Однако, Object.clone() бросает CloneNotSupportedException если объект не является экземпляром класса, реализующего интерфейс маркера Cloneable.

Реализация по умолчанию Object.clone() выполняет неглубокую копию . Когда классу требуется глубокая копия или какое-либо другое пользовательское поведение, он должен реализовать это самостоятельно. clone() метод после того, как они получат копию из суперкласса.

Синтаксис для вызова clone в Java (при условии, что obj — это переменная типа класса, имеющая общедоступное clone() метод):

Object copy = obj.clone();

или обычно

MyClass copy = (MyClass) obj.clone();

который обеспечивает приведение типов, необходимое для назначения общего Object ссылка вернулась из clone на ссылку на MyClass объект.

Один из недостатков конструкции clone() метод заключается в том, что возвращаемый тип clone() является Object, и его необходимо явно привести обратно к соответствующему типу. Однако, переопределив clone() возврат соответствующего типа предпочтительнее и устраняет необходимость приведения типов в клиенте (с использованием ковариантных типов возврата , начиная с J2SE 5.0).

Еще одним недостатком является то, что часто невозможно получить доступ к clone() метод абстрактного типа. Большинство интерфейсов и абстрактных классов в Java не определяют общедоступный clone() метод. В результате часто clone() Метод можно использовать только в том случае, если известен фактический класс объекта, что противоречит принципу абстракции, согласно которому используется наиболее универсальный тип. Например, если у человека есть List ссылку в Java нельзя вызвать clone() по этой ссылке, потому что List не указано публичное clone() метод. Фактические реализации List нравиться ArrayList и LinkedList вообще у всех есть clone() сами методы, но носить с собой фактический тип класса объекта неудобно и это плохая абстракция.

Альтернативы

[ редактировать ]

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

Также использование сериализации и десериализации является альтернативой использованию клонирования.

Шаблон синглтон

[ редактировать ]

При написании класса с использованием шаблона Singleton одновременно может существовать только один экземпляр этого класса. В результате классу не должно быть разрешено создавать клоны. Чтобы предотвратить это, можно переопределить clone() метод, используя следующий код:

public Object clone() throws CloneNotSupportedException {
    throw new CloneNotSupportedException();
}

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

Иерархия классов

[ редактировать ]

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

1) Каждый тип, который необходимо клонировать, должен иметь общедоступный метод clone() в своем собственном классе или общедоступный метод clone() в одном из его родительских классов.

Пример:

Чтобы вызвать clone() для переменной varY1, которая имеет тип Y, Y или родительский объект Y должен объявить общедоступный метод clone(). Здесь родительский класс X предоставляет общедоступный метод clone().

public class X implements Cloneable {
    public X clone() throws CloneNotSupportedException {
        return (X) super.clone();
    }
}

public class Y extends X { }

public class Z extends Y { }

public class test1 {
    public void function() throws CloneNotSupportedException {
        Y varY1 = new Z();
        Y varY2 = (Y) varY1.clone();
    }
}

2) Каждый класс, реализующий clone(), должен вызывать super.clone() для получения ссылки на клонированный объект. Если в классе есть какие-либо ссылки на объекты, которые также необходимо клонировать (например, при глубоком копировании), то метод clone() должен выполнить все необходимые изменения объекта перед его возвратом. (Поскольку Object.clone() возвращает точную копию исходного объекта, любые изменяемые поля, такие как коллекции и массивы, будут совместно использоваться оригиналом и копией, что в большинстве случаев не является ни ожидаемым, ни желательным.)

Пример:

Поскольку класс Z содержит ссылку на объект, его метод clone() также клонирует эту ссылку на объект, чтобы вернуть глубокую копию оригинала.

public class X implements Cloneable {
    public X clone() throws CloneNotSupportedException {
        return (X) super.clone();
    }
}

public class Y extends X { }

public class ObjectABC implements Cloneable {
    public ObjectABC clone() throws CloneNotSupportedException {
        return (ObjectABC) super.clone();
    }
}

public class Z extends Y {
    private ObjectABC someABC;

    public Z clone() throws CloneNotSupportedException {
        Z newZ = (Z) super.clone();
        newZ.someABC = someABC.clone();

        return newZ;
    }
}

public class test1 {
    public void function() throws CloneNotSupportedException {
        Y varY1 = new Z();
        Y varY2 = (Y) varY1.clone();
    }
}

Подводные камни

[ редактировать ]

Если каждый класс в иерархии реализует clone() все эти функции будут вызываться при клонировании, что добавляет некоторые накладные расходы. За многие итерации эти накладные расходы могут стать значительными.

В случае сложных графов объектов глубокое копирование также может стать проблематичным, если существуют рекурсивные ссылки.

Не всегда целесообразно иметь несколько копий одного и того же объекта. Если цель конкретной clone() реализация не до конца понятна потребителям, она может непреднамеренно нарушить парадигму «один объект, множественные ссылки».

Заключительные поля

[ редактировать ]

В целом, clone() несовместимо с final поля. Потому что clone() по сути является конструктором по умолчанию (тот, у которого нет аргументов), невозможно назначить final поле внутри clone() метод; Результатом является ошибка компилятора. Если значение поля является неизменяемым объектом, это нормально; просто позвольте «конструктору» скопировать ссылку, и оригинал, и его клон будут использовать один и тот же объект.

Но если значение является изменяемым объектом, его необходимо глубоко скопировать. Одним из решений является удаление final модификатор с поля, отказываясь от преимуществ, предоставляемых модификатором.

По этой причине некоторые программисты предлагают сделать объекты в иерархии сериализуемыми и создавать копии путем сериализации старого объекта и последующего создания нового объекта из результирующего битового потока , который правильно обрабатывает конечные элементы данных, но работает значительно медленнее. [1]

В качестве альтернативы можно вернуть совершенно новый объект из полей текущих объектов, что можно сделать сначала вызовом конструктора, а затем назначением нефинальных полей. Другой альтернативный метод фактически заключается в формальном оформлении идеи: создании конструктора копирования, принимающего экземпляр. Фактически, это то, что некоторые люди рекомендуют вместо клонирования. [2]

  1. ^ Миллер, Дэйв (6 августа 1999 г.). «Совет 76 по Java: альтернатива технике глубокого копирования» . JavaWorld . Проверено 14 июля 2020 г.
  2. ^ Clone() против конструктора копирования — рекомендуется в Java , StackOverflow
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: 3cb051445423b6b47d5bb9696509c0c4__1686124260
URL1:https://arc.ask3.ru/arc/aa/3c/c4/3cb051445423b6b47d5bb9696509c0c4.html
Заголовок, (Title) документа по адресу, URL1:
clone (Java method) - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)