Эффективная Java. Клонируемый интерфейс
Я читаю книгу "Эффективная Java" и не понимаю ни одного абзаца, где объясняется интерфейс Clonable. Может кто-нибудь объяснить мне этот пункт:
... программисты предполагают, что если они расширяют класс и вызывают
super.clone
из подкласса возвращаемый объект будет экземпляром подкласса. Единственный способ, которым суперкласс может предоставить эту функциональность, - это вернуть объект, полученный путем вызоваsuper.clone
, Если метод clone возвращает объект, созданный конструктором, у него будет неправильный класс.
Благодарю.
3 ответа
Я должен отметить, что для начала clone
сам по себе сломан, и что конструктор копирования, как Sheep(Sheep cloneMe)
это гораздо более элегантная идиома, чем clone
, принимая во внимание Cloneable
Контракт очень слабый. Вы, наверное, уже знаете это, так как вы читаете книгу, но это стоит вставить сюда.
Во всяком случае, чтобы ответить на вопрос:
Object.clone()
создаст объект того же типа, что и объект, к которому он был вызван. По этой причине настоятельно рекомендуется "каскадировать" до Object
для получения результата вы планируете вернуть. Если кто-то решит не следовать этому соглашению, вы получите объект типа класса, который нарушил соглашение, что вызовет множество проблем.
Для иллюстрации у меня есть такой класс
class Sheep implements Cloneable {
Sheep(String name)...
public Object clone() {
return new Sheep(this.name); // bad, doesn't cascade up to Object
}
}
class WoolySheep extends Sheep {
public Object clone() {
return super.clone();
}
}
Вдруг, если я сделаю
WoolySheep dolly = new WoolySheep("Dolly");
WoolySheep clone = (WoolySheep)(dolly.clone()); // error
Я получу исключение, потому что я получаю от dolly.clone()
это Sheep
не WoolySheep
,
Я не согласен с ответом @corsiKa. начиная с Java5.0. Java поддерживает ковариантный тип возвращаемого значения, поэтому правильная реализация clone() должна быть:
class Sheep implements Cloneable {
Sheep(String name)...
public Sheep clone() {
return new Sheep(this.name);
}
}
class WoolySheep extends Sheep {
public WoolySheep clone() {
return super.clone(); // compile time error, Type miss match.
}
}
Также предложенный альтернативный конструктор копирования не поддерживает полиморфизм. рассмотрим следующий пример (который не может сделать конструктор копирования):
interface Animal implements Cloneable {
String whatAreYou()
}
class Cat implements Animal {
String whatAreYou() {
return "I am a cat";
}
Cat clone() {
return new Cat();
}
}
class Dog implements Animal{
String whatAreYou() {
return "I am a dog";
}
Dog clone() {
return new Dog();
}
}
class Lib {
Animal cloneAnimal(Animal animal) {
return animal.clone();
}
}
class A {
protected Object clone() {
return new A();
}
}
class B extends A implements Cloneable {
public Object clone() {
return super.clone();
}
}
Вот, A
имеет неверную реализацию clone
потому что это вызовет исключение:
B obj = (B)(new B()).clone();
Вместо, A.clone()
должен позвонить super.clone()
вместо конструктора. Object.clone()
затем сгенерирует новый объект типа времени выполнения вместо типа времени компиляции.
Любые поля затем клонируются на этот новый объект. Было бы заманчиво использовать конструктор, если у вас уже есть тот, который инициализирует все ваши поля (например, конструктор копирования), но это приведет к некорректному поведению для любых подклассов.
Если класс final
тогда это не имеет значения, так как не может иметь подклассов.