stringtranslate.com

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

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

Обзор

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

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

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

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

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

Копия объекта = obj.clone ( ) ;   

или обычно

MyClass копия = ( MyClass ) объект.клон ( ) ;    

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

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

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

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

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

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

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

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

public Object clone () выдает 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 реализует Cloneable { public X clone () выдает исключение CloneNotSupportedException { return ( X ) super . clone (); } }               публичный класс Y расширяет X { }      публичный класс Z расширяет 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 реализует Cloneable { public X clone () выдает исключение CloneNotSupportedException { return ( X ) super . clone (); } }               публичный класс Y расширяет X { }      public class ObjectABC implements Cloneable { public ObjectABC clone () throws CloneNotSupportedException { return ( ObjectABC ) super.clone ( ) ; } }               открытый класс Z расширяет Y { закрытый ObjectABC someABC ;         public Z clone () выдает исключение CloneNotSupportedException { Z newZ = ( Z ) super . clone (); newZ . someABC = someABC . clone ();              вернуть новыйZ ; } }  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 г.). «Java Tip 76: Альтернатива технике глубокого копирования». JavaWorld . Получено 14 июля 2020 г. .
  2. ^ Clone() против Copy-конструктора — что рекомендуется в Java, StackOverflow

Внешние ссылки