Подтвердить что ты не робот

Java: метод super.clone() и наследование

У меня есть быстрый вопрос относительно метода clone() в Java, который используется как super.clone() в отношении наследования, - где я вызываю метод clone() в родительском классе, начиная с кнопки.

Предполагается, что метод clone() возвращает копию этого объекта, однако, если у меня есть три класса в наследстве heirachy и вызывают super.clone() три раза, почему не самый высокий класс в наследстве heirachy, как раз под class Object, получить копию этого класса?

Предположим, что мы имеем три класса: A, B и C, где A → B → C (inherit = → )

Затем вызывая super.clone() в классе C, вызывает clone() в B, который вызывает super.clone(), вызывает clone() в A, который вызывает super.clone() ', на этот раз Object.clone() получает вызов'. Почему это не копия объекта this в отношении класса A, который возвращается из Object.clone()? Это звучит логично для меня.

4b9b3361

Ответ 1

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

  • Похоже, вы сбиты с толку о том, как clone() обычно реализуется.

  • Похоже, вы думаете, что клонирование - хорошая идея (против использования конструктора копирования, фабрик или их эквивалента).

Вот пример реализации метода clone: ​​

@Override 
public Object clone() throws CloneNotSupportedException {   
    //get initial bit-by-bit copy, which handles all immutable fields
    Fruit result = (Fruit)super.clone();

    //mutable fields need to be made independent of this object, for reasons
    //similar to those for defensive copies - to prevent unwanted access to
    //this object internal state
    result.fBestBeforeDate = new Date( this.fBestBeforeDate.getTime() );

    return result;
}

Обратите внимание, что результат super.clone() сразу отображается на Fruit. Это позволяет методу наследования затем модифицировать специфичные для Фрукта данные члена (fBestBeforeDate в этом случае).

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

Теперь, что более важно, клонирование - плохая идея. Копировальные конструкторы и фабрики предоставляют гораздо более интуитивные и легко поддерживаемые альтернативы. Попробуйте прочитать заголовок на Java Practices, который я приложил к примеру: это суммирует некоторые из проблем. Джош Блох также гораздо более продолжительное обсуждение: клонирование обязательно следует избегать. Вот отличный краткий абзац о том, почему он считает клонирование проблемой:

Метод клонирования объектов очень сложный. Он основан на полевых копиях и это "экстралингвистический". Он создает объект, не вызывая конструктор. Нет никаких гарантий, что он сохраняет инварианты установленных конструкторами. Было много ошибок над лет, как в Солнце, так и за его пределами, что связано с тем, что если вы просто вызовите super.clone повторно вверх по цепочке, пока вы не клонируете объект, у вас есть мелкая копия объекта. Клон обычно разделяет состояние с клонированным объектом. Если это состояние изменчиво, у вас нет двух независимых объектов. Если вы измените один, другой изменения также. И вдруг вы получаете случайное поведение.

Ответ 2

Свой специальный нативный метод. Это было сделано для облегчения клонирования. В противном случае вам придется скопировать весь код классов вашего предка.

Ответ 3

Хотя один ответ принят, я не думаю, что он полностью отвечает на первую часть вопроса (почему всегда работает downcasting в подклассах). Хотя я не могу это объяснить, я думаю, что смогу прояснить некоторые путаницы в плакате, которые были такими же, как и мои. Имеются следующие классы

class A implements Cloneable 
{
   @Override
   protected A clone() throws CloneNotSupportedException // could be public
   { 
      Object clone = super.clone();
      System.out.println("Class A: " + clone.getClass()); // will print 'C'
      return (A) clone;
   }
}

class B extends A
{
   @Override
   protected B clone() throws CloneNotSupportedException
   { 
      A clone = super.clone();
      System.out.println("Class B: " + clone.getClass()); // will print 'C'
      return (B) clone;
   }
}

class C extends B
{
   @Override
   protected C clone() throws CloneNotSupportedException
   { 
      B clone = super.clone();
      System.out.println("Class C: " + clone.getClass()); // will print 'C'
      return (C) clone;
   }
}

static main(char[] argv)
{
   C c = new C();
   C cloned_c = c.clone();
}

Результатом этого является то, что

Class A: C

Class B: C

Class C: C

печатается в командной строке. Итак, по сути, метод clone() Object каким-то образом может смотреть вниз на стек вызовов и видеть, какой тип объекта в начале вызываемой цепочки clone(), тогда при условии, что вызовы пузырятся так, что Object#clone() фактически вызывается, создается объект этого типа. Так что это происходит уже в классе C, что странно, но это объясняет, почему downcasts не приводят к ClassCastException. Я проверил с OpenJDK, и, похоже, это происходит с помощью черной магии Java, реализованной в собственном коде.

Ответ 4

Если clone() в B возвращает любой clone() в возвращает и clone() в C возвращает то, что возвращает clone() в B, тогда clone() в C вернет то, что возвращает clone() в A.