有效的Java。可克隆的界面

时间:2012-07-25 16:16:46

标签: java cloneable effective-java

我阅读了有效的Java书,并且不了解一个解释Clonable接口的段落。有人可以解释一下这段话:

  

...程序员假设他们扩展一个类并调用它   来自子类的super.clone,返回的对象将是一个实例   的子类。超类可以提供此功能的唯一方法   功能是返回通过调用super.clone获得的对象。   如果clone方法返回由构造函数创建的对象,它将会   有错误的班级。

感谢。

3 个答案:

答案 0 :(得分:6)

我应该首先注意clone本身已被打破,并且Sheep(Sheep cloneMe)之类的复制构造函数比clone更为优雅,考虑到{ {1}}合同非常薄弱。你可能已经知道了这一点,因为你正在阅读这本书,但值得一试。

无论如何,要回答这个问题:

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

答案 1 :(得分:2)

我不同意@ 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();
  }
}

答案 2 :(得分:1)

class A {
    protected Object clone() {
        return new A();
    }
}

class B extends A implements Cloneable {
    public Object clone() {
        return super.clone();
    }
}

此处A的{​​{1}}实施无效,因为这会引发异常:

clone

相反,B obj = (B)(new B()).clone(); 必须调用A.clone()而不是构造函数。然后super.clone()将生成运行时类型的新对象,而不是编译时类型。

然后将任何字段克隆到此新对象上。如果您已经有一个初始化所有字段(如复制构造函数),那么使用构造函数很有诱惑力,但这会导致任何子类的行为不正确。

如果类是Object.clone(),那么它没关系,因为它不能有任何子类。