我读过《Effective Java》一书,但不明白其中解释 Clonable 接口的一段。有人能给我解释一下这一段吗:

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

谢谢。

有帮助吗?

解决方案

我首先要注意的是 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, ,那么没关系,因为它不能有任何子类。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top