有效的Java。可克隆接口
-
11-12-2019 - |
题
我读过《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
, ,那么没关系,因为它不能有任何子类。