【问题标题】:Why Object clone() method available only to classes that implement Cloneable interface? [duplicate]为什么 Object clone() 方法仅适用于实现 Cloneable 接口的类? [复制]
【发布时间】:2013-08-16 21:29:57
【问题描述】:

我知道clone() 是一个受保护的方法,但“受保护”意味着它可以被特定类的所有子类访问。

任何Java类都是Object的子类,那么这里的protected方法是什么原因呢?

为什么我们只能在实现Cloneable 接口的类上调用clone()? 我不明白它与Object 中的clone() 被声明为受保护这一事实有何关联。

【问题讨论】:

  • 这是一个悲伤的故事.. :) 在 java 中的克隆不能正常工作(嗯,确实可以,但经过很多努力),所以避免它。
  • Cloneable 实际上是一个非常糟糕的接口,不应该被视为一个很好的例子。老实说,它主要靠魔法起作用。
  • 阅读 Josh Bloch 的 Effective Java 了解为什么 Cloneable 和 clone() 被破坏了。请改用复制构造函数或类似的解决方案。

标签: java object cloneable


【解决方案1】:

Object 的 clone() 方法非常特殊,因为它总是返回当前类的实例,该实例复制了所有字段(甚至是最终字段)。我不认为用普通的 Java 代码来重现它是不可能的,即使是反射也不可能。

因此,它必须对所有类都可用,但由于您不希望所有内容都可克隆,因此默认情况下不应从外部调用它,因此必须对其进行保护。

作为附加检查,clone 检查该类是否实现了Cloneable,只是为了确保您不会意外克隆不可克隆。


总而言之,克隆有点坏,因为当您需要深度复制最终字段时它不起作用。我建议您按照这种模式手动实现实例复制。

public class Base {

    /** one or more public constructors */
    public Base() { ... }

    /** copy-constructor */
    protected Base(Base src) { /* copy or deep-copy the state */ }

    public Base copy() { return new Base(this); }
}

public class Derived extends Base {

    /** one or more public constructors */
    public Derived() { ... }

    /** copy-constructor */
    protected Derived(Derived src) { 
        super(src);
        /* copy or deep-copy the state */ 
    }

    @Override
    public Derived copy() { return new Derived(this); }
}

【讨论】:

    【解决方案2】:

    因为这是他们设计的方式。在 Bug Parade 的某个地方有一句话说设计的最初原因“迷失在时间的迷雾中”,但我记得 Sun 在 1990 年代的一次讨论中说设计提供了四种可能的行为。

    不记得细节了:-|但你可以解决:

    1. 什么都不做。类不能被克隆,clone() 方法只能被派生类访问。
    2. 实现Cloneable: 类可以被克隆,但clone() 方法除了派生类之外是不可访问的。
    3. 实现 Cloneable 并提供您自己的 clone() 方法:与 (2) 一样,但有可能将 clone() 公开,现在也可以返回正确的类型。
    4. 派生自执行 (2) 或 (3) 的类,实现 clone(),然后抛出 NotCloneableException:,您将返回 1。

    clone() 放入界面会丢失一些内容。

    【讨论】:

    • 我喜欢“迷失在时间的迷雾中”。 :)
    • 这个答案被低估了。
    【解决方案3】:

    Object.clone() 的默认实现是对象中的基类型方法可以生成该对象相同类型的新对象的唯一方法(可能是反射除外)。另一方面,对于许多类,派生类不可能实现不会违反基类不变量的工作clone 方法。希望不允许此类派生类调用clone 并接收不可能使用的对象。

    回想起来,正确的做法可能是定义一个系统 CloneableObject,它继承自 Object,其中包括受保护的 memberwiseclone 方法(就像 Object.clone() 现在所做的那样),仅此而已,并让所有可克隆的类都继承自它作为它们的基本类型。该类型可以在其clone() 方法中使用一些其他类型无法使用的特殊魔法。然而,系统使用Cloneable 接口的存在来决定是否应允许对象使用基本级别的clone() 方法。不是一个伟大的设计,但它就是这样。很难说这比允许几乎所有派生类调用 MemberwiseClone 的 .NET 方法好还是坏,无论派生类是否有任何方法可以这样做而不会产生损坏的对象。

    【讨论】:

      【解决方案4】:

      Cloneable 是一个接口,为了克隆对抽象基类的引用,基类必须具有 clone() 方法,或者其父类之一必须具有可公开访问的 clone() 方法。 Cloneable 只是充当一个标记接口。

      请参考:

      【讨论】:

      • 但是Cloneable接口是如何连接到clone方法被保护的呢?
      • 答案请参考链接:stackoverflow.com/questions/1138769/…
      • 我再重复一遍:我知道 clone() 是一个受保护的方法,但“受保护”意味着它可以被特定类的所有子类访问。任何Java类都是Object的子类,那么这里的protected方法是什么原因呢?共同点 - 在 Object 中使用受保护方法的原因是什么?
      • @Don_Quijote 这意味着你可以在你的类中重写它,如果你想要但是它不能直接访问,因为它不是public。如果您检查重复的问题,您将有更好的理解。
      • @Don_Quijote: protected 意味着它可以被同一个类中的代码访问,但不能被类外的代码访问。这就是在Object 中拥有受保护方法的原因——这样类可以在内部使用该方法,但外部代码不能在其上调用该方法。而Cloneable 接口与Object.clone() 受保护这一事实完全无关。 Cloneable 接口只影响Object.clone() 是否会抛出异常。
      【解决方案5】:

      http://docs.oracle.com/javase/7/docs/api/java/lang/Cloneable.html

      一个类实现了 Cloneable 接口,以向 Object.clone() 方法指示该方法制作该类实例的逐个字段副本是合法的。 在未实现 Cloneable 接口的实例上调用 Object 的 clone 方法会导致抛出异常 CloneNotSupportedException。

      按照惯例,实现此接口的类应使用公共方法覆盖 Object.clone(受保护的)。有关覆盖此方法的详细信息,请参阅 Object.clone()。

      请注意,此接口不包含克隆方法。因此,不可能仅仅凭借实现该接口的事实来克隆对象。即使以反射方式调用 clone 方法,也不能保证它会成功。

      即使 Object 具有受保护的方法 clone Object 未实现 Cloneable,但提出您的问题,但被称为 clone() 方法的对象必须实现 Cloneable interface

      【讨论】:

      • 好的,但是为什么锥方法是受保护的?
      • -1:您似乎在引用 API 文档,但在该页面的任何地方都找不到您在引用标签中添加的文字。他们是不正确的。
      • @kdgregory 感谢kd的关注,我添加了文档中的所有行,之前我只添加了第一点,请检查。
      【解决方案6】:

      可克隆的是marker interface。 clone() 方法不是由 Cloneable 接口定义的。

      Object 类中的 clone 方法受到保护,以防止客户端类调用它 - 只有子类可以调用或覆盖 clone,这样做是 bad idea

      【讨论】:

        猜你喜欢
        • 2017-12-26
        • 2020-07-03
        • 1970-01-01
        • 2012-01-28
        • 2019-08-19
        • 2013-01-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多