【问题标题】:Effective Java. Clonable interface有效的Java。可克隆界面
【发布时间】:2012-07-24 03:31:45
【问题描述】:

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

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

谢谢。

【问题讨论】:

  • 就我而言,你永远不应该使用克隆,你最好使用复制构造函数。至于克隆去这里看:stackoverflow.com/questions/2326758/…
  • @mihaisimi 复制构造函数不允许运行时动态类型克隆。例如,请克隆List list,你要使用哪个List实现的构造函数,ArrayListLinkedList
  • Clonable 接口是整个 Java 语言中最丑陋和最愚蠢的东西之一。我不知道他们为什么不弃用它。使用起来真的很痛苦,它带来的问题比它解决的问题多得多。有时需要它的功能,但它的实现是一场灾难。
  • @SoboLAN clone 在正确实施时效果很好。问题是大多数人都搞砸了。

标签: java cloneable effective-java


【解决方案1】:

首先我应该注意到clone 本身是损坏的,并且考虑到Cloneable 合约非常弱,复制构造函数,如Sheep(Sheep cloneMe)clone 优雅得多.您可能已经知道这一点,因为您正在阅读这本书,但值得在这里介绍。

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

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

【讨论】:

  • @jtahlborn 正如问题所问的那样,它假设Sheep.clone() 使用给定的构造函数实现不正确。我已经对其进行了编辑以使其明确。
  • 重要的是cloneing 是不道德的:-)
  • 嗯。谢谢,但我仍然不明白一个想法。 First Bloch 解释说,使用子类中的super.clone 我们无法获得“子类类型”的对象。但是我们可以在超类上实现这个调用super.clone。逻辑在哪里?...
  • @MyTitle 如果我们一直级联到Object.clone(),那么clone() 调用的结果将是与被调用对象相同的类,因为Object.clone() 就是这样做的.相反,如果有人决定不级联到Object.clone(),并返回使用构造函数调用的东西,他们将获得一个类型介于被调用对象和Object 之间的对象,这不是有意的。
  • @MyTitle - 你误读了书中的引文。使用super.clone正确的实现。
【解决方案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();
  }
}

【讨论】:

    【解决方案3】:
    class A {
        protected Object clone() {
            return new A();
        }
    }
    
    class B extends A implements Cloneable {
        public Object clone() {
            return super.clone();
        }
    }
    

    这里,Aclone 的实现无效,因为这会引发异常:

    B obj = (B)(new B()).clone();
    

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

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

    如果类是final,那么没关系,因为它不能有任何子类。

    【讨论】:

    猜你喜欢
    • 2013-02-20
    • 2016-06-27
    • 2019-09-02
    • 2014-12-13
    • 2011-04-25
    • 2011-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多