Эффективная Java: анализ метода clone()

Рассмотрите следующее из Эффективного Java-элемента 11 (разумно переопределите клон), где Джош Блох объясняет, что не так с контрактом clone().

С этим контрактом существует ряд проблем. Постановление о том, что "нет конструкторы называются" слишком сильными". Хорошо выполненный метод клонирования может вызывать конструкторы для создания объектов, встроенных в построенный клон. Если класс final, clone может даже вернуть объект, созданный конструктором.

Может кто-нибудь объяснить, что говорит Джош Блох в первом абзаце: "Если класс final, clone может даже вернуть объект, созданный конструктором". Что final связано с clone() здесь?

Ответы

Ответ 1

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

Ответ 2

Это потому, что типичные реализации clone() выглядят следующим образом:

public class MyClass implements Cloneable {
  protected Object clone() {
    MyClass cloned = (MyClass) super.clone();
    // set additional clone properties here
  }
}

Таким образом, вы можете наследовать поведение клонирования из вашего суперкласса. Он широко предположил, что результат операции clone() вернет правильный тип экземпляра на основе объекта, на который он был вызван. То есть. this.getClass()

Итак, если класс является окончательным, вам не нужно беспокоиться о подклассе, вызывающем super.clone(), и не возвращать правильный тип объекта.

public class A implements Cloneable {
    public Object clone() {
       return new A();
    }
}


public class B extends A {
    public Object clone() {
       B b = (B)super.clone(); // <== will throw ClassCastException
    }
}

Но, если A является окончательным, никто не может его расширять и, следовательно, безопасно использовать конструктор.

Ответ 3

Для того, чтобы быть клонированным, класс не должен обеспечивать свою собственную реализацию clone. Он может делегировать это своему клонируемому суперклассу. Здесь идет catch: clone должен всегда возвращать экземпляр того же класса, что и вызываемый экземпляр. Это невозможно достичь в описанном случае, если вызывается явный конструктор. Если класс overridng clone является окончательным, с другой стороны, это будет нормально.

Ответ 4

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