1

我阅读了 Effective Java 书,但不理解解释 Clonable 接口的一段。有人可以解释一下这一段吗:

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

谢谢。

4

3 回答 3

6

首先我要注意clone的是,它本身就被破坏了,并且考虑到合同非常弱,复制构造函数 likeSheep(Sheep cloneMe)是一个比 更加优雅的习语。您可能已经知道这一点,因为您正在阅读这本书,但值得将其放在这里。cloneCloneable

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

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()是 a Sheep,而不是 a WoolySheep

于 2012-07-25T16:21:24.427 回答
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();
  }
}
于 2017-10-31T22:09:20.067 回答
1
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,那么没关系,因为它不能有任何子类。

于 2012-07-25T16:29:46.407 回答