Клонирование объектов в Java

Клонирование объекта бывает 2-х видов:

  • Поверхностное (shallow);
  • Глубокое (deep);

Чтобы объект можно было клонировать, он должен реализовывать интерфейс Сloneable (маркер). Использование интерфейса влияет на поведение метода (clone) родительского класса (Object). Таким образом, вызов метода MyType.clone() создаст новый представитель типа MyType. Это будет shallow клонирование:

При глубоком клонирование рисунок будет выглядеть, так:

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

class MyType implements Cloneable  {
      SomeType someType;
      AnotherType anotherType;

      public Object clone() {
            MyType newObject = (MyType) super.clone();

            // Теперь необходимо также клонировать все ссылочные поля
            newObject.someType = this.someType.clone();
            newObject.anotherType = this.anotherType.clone();
      }
}

При клонировании в глубину нельзя использовать конструктор:

newObject.someType = new SomeType(); // замена this.someType.clone();

т.к. это приводит к неправильному клонированию, если поле содержит ссылку на подтип типа SomeType (т.е. OtherSomeType extends SomeType) в итоге поле будет типа SomeType вместо OtherSomeType. Таже проблема наблюдается, когда мы расширяем область видимости метода с protected на public:

public class BaseClass implements Cloneable {
        public BaseClass (/* parameters */) {
            // Code goes here...
        }
        // Rest of the class.
        public Object clone () throws CloneNotSupportedException {
            return new BaseClass (/* parameters */);
        }
}

Если мы создадим подтип OtherBaseClass extends BaseClass и решим OtherBaseClass клонировать в глубину, то произойдет исключение, т.к. вызов super.clone() всегда будет возвращать BaseClass вместо OtherBaseClass.

public Object clone () throws CloneNotSupportedException {
            // The cast in the line below throws an exception!
            // т.к. вернется BaseClass, а не OtherBaseClass
            OtherBaseClass newObject = (OtherBaseClass)super.clone();

            if (this.data != null)
                newObject.data = (SomeOtherClass)this.data.clone();

            return newObject;
}

Вывод: всегда, при глубоком клонировании пользоваться super.memberField.clone()

за статью спасибо,
Я понимаю, что статья писалась давно, и что с тех времен много воды утекло, тем не менее, насколько я понял "поверхностное" это "shallow" (а не shadow, как написано в заголовке)
И по поводу переопределения метода clone - в интерфейсе Cloneable четко сказано
"By convention, classes that implement this interface should override
Object.clone (which is protected) with a public method."
(ну это просто ответ на вопросы некоторых гостей

Спасибо за поправку, действительно, должно быть shallow copy.

Да, спасибо, полезная статья! Вот только непонятно, по-поводу того, что использовать clone с модификатором protected можно только из подклассов. Но если все классы являются по умолчанию подклассами Object, в котором, собственно и реализован этот метод, то получается, что и использовать его также можно во всех классах? Тогда мне опять-таки непонятен смысл переопределения clone как public!

Переопределять, как public нужно в случае, если метод нужно дернуть извне клонируемого класса. Да, все классы наследуются от Object, но, т.к. метод clone() является protected, вызвать его можно только, чтобы клонировать самого себя.

Как это все сложно, для меня это нереально запомнить

спасибо а материал, а то что то не садился за яву со времен первого курса универа

У вот у меня проблема. У меня "массивный") объект, в которов приходится клонировать кучу обектов, которые он содержит в себе... но проблема в том, что один из объектов является коллекцией, где каждый элемент имеет связи с некоторыми другими элементами этой же коллекции и при клонировании происходит зацикливание. Клонируется элемент и клонируются его соседи и так далее...

А можно пример когда с модификатором public - работает, а с protected - нет?

И все-таки хотелось бы уточнений. В Object метод clone() объявлен protected. Почему требуется переопределять его как public, если и так он доступен в подклассах?
Заранее спасибо.

Можно оставить как protected, но тогда клонировать объект можно будет только из подклассов.

спасибо большое, разобрался

Антон Викторович, я иду есть Вашу трудовую!!!

Полезная статейка. Всем читать и запоминать )

Отправить комментарий

Image CAPTCHA
Enter the characters shown in the image.