为什么clone()方法在java.lang.Object中受到保护?


Answers:


107

克隆受到保护的事实非常令人怀疑-事实是该clone方法未在Cloneable接口中声明。

它使该方法对于获取数据副本非常无用,因为您不能说

if(a instanceof Cloneable) {
    copy = ((Cloneable) a).clone();
}

我认为,Cloneable现在的设计在很大程度上被认为是一个错误(以下引用)。我通常希望能够实现一个接口的实现,Cloneable不一定要实现该接口Cloneable(类似于的使用Serializable)。没有反思就无法做到这一点:

ISomething i = ...
if (i instanceof Cloneable) {
   //DAMN! I Need to know about ISomethingImpl! Unless...
   copy = (ISomething) i.getClass().getMethod("clone").invoke(i);
}

乔什·布洛赫(Josh Bloch)的《有效Java 》的引文:
“ Cloneable接口旨在作为对象的混合接口,以宣告它们允许克隆。不幸的是,它不能满足这一目的……这是一种非常不典型的接口使用,而不是一个可被模仿的接口......为了实现这些接口有一个类有任何影响,它和所有超类必须遵守一个相当复杂的,不能强制执行,并在很大程度上无证协议


2
如果对象不是可克隆的,则对象的clone()将抛出CloneNotSupportedException。因此,如果要调用super.clone()(确实会导致Object.clone()),则确实需要Cloneable。我看不到没有实现Serializable即可如何序列化对象。
Steve Kuo

1
“我认为现在可克隆的设计在很大程度上被认为是一个错误。” [需要引用]
凯文·潘科

抱歉-我不是在暗示。我只是在暗示“好的”设计是扩展接口Serializable-由实现决定是否实现Serializable。我将其扩展为Cloneable-这不是接口应该扩展的内容-但是接口的实现是自由的Cloneable。麻烦的是,如果您具有接口类型的参数,则询问它是否可克隆;但是您实际上无法克隆它!
oxbow_lakes

6
@Kevin- Josh Bloch的有效Java pp45。“ Cloneable接口旨在用作对象的混合接口,以
宣告

也在同一页上:“这是一种非常不典型的接口用法,而不是要被模仿的接口”“为了实现接口对类有任何影响,它及其所有超类必须服从相当复杂的方法,不可执行且很大程度上未记录
在案的

30

Clonable接口只是一个标记,表明该类可以支持克隆。该方法受到保护,因为您不应该在对象上调用它,您可以(并且应该)将其重写为公共方法。

从太阳出发:

在Object类中,clone()方法被声明为protected。如果您所做的只是实现Cloneable,则只有同一个包的子类和成员才能在对象上调用clone()。要使任何包中的任何类都可以访问clone()方法,您必须重写它并将其声明为公共,如下所示。(重写方法时,可以使它的私有性降低,但不能使其私有化。这里,Object中受保护的clone()方法将被重写为公共方法。)


好的,直到您将接口引入混合中为止 –尝试克隆未知的实现Set
oxbow_lakes 2009年

@oxbow_lakes:但也许Set的某些实现不可
克隆

3
您不能克隆任何未实现Clonable接口的东西,它是一个标记为“该类可正确克隆”的标记,非常类似于Serializable接口。顺便说一句,有一种方法可以很好地通过序列化克隆类,例如Google之类的“ java序列化克隆”,您可能会找到几种获取对象深层副本的方法。
比尔K 2009年

4
您不能克隆任何未实现Cloneable接口的东西,而仅仅是因为某些东西实现了Cloneable接口并不意味着您可以对其进行克隆。
迈克尔·迈尔斯

1
@BuckCherry toString具有默认实现,如果您调用它,则会发生一些好的事情,并且您将获得一个字符串。equals具有默认实现(与==相同)。克隆不能具有默认实现。如果在尚未实现克隆的对象上调用克隆,则不会获得合理的行为。克隆很复杂,不能真正自动完成(某些没有默认构造函数的对象可能无法进行一般性克隆),因此默认情况下它们会使安全性提高一点。我认为完全没有必要将其放在Object上。
比尔K

7

clone之所以受到保护,是因为它是应该被覆盖的东西,因此它特定于当前类。尽管有可能创建一个clone完全克隆任何对象的公共方法,但这并不像为需要它的类专门编写的方法那样好。


但是为什么要为此保护它呢?
Janusz

3
它受到保护,因此您不必使用in in对象(无论如何它只会抛出异常)。他们希望您在课程中覆盖它,然后将其公开。(下面也回答了几次)
Bill K

1
当按照我下面的观点考虑接口时,它是无用的
oxbow_lakes

应该被覆盖不是很清楚,那么它应该是抽象的,然后不能在对象类中,我正在努力理解为什么会这样。
Kumar Abhishek

4

Clone方法不能直接在任何对象上使用,这就是为什么要被子类覆盖的原因。

当然,它可能是公开的,并且在无法克隆时会抛出一个适当的例外,但是我认为这会产生误导。

现在实现克隆的方式使您考虑为什么要使用克隆以及如何克隆对象。


2

之所以受到保护,是因为默认实现会绕过构造函数对所有字段(包括私有字段)进行浅成员复制。最初,这不是一个对象可以设计用来处理的东西(例如,它可能会在共享列表中跟踪创建的对象实例,或者类似的东西)。

出于同样的原因,clone()如果调用的对象未实现,则默认的实现会抛出Cloneable。这是一种潜在的不安全操作,会产生深远的后果,因此该类的作者必须明确选择加入。


实际上,默认实现(在对象中)根据文档引发了异常...
比尔K,

2
不,它不只是抛出。在其JavaDoc中:“类Object的方法克隆执行特定的克隆操作。首先,如果此对象的类未实现接口Cloneable,则抛出CloneNotSupportedException。请注意,所有数组都被视为实现了接口Cloneable。否则,此方法将创建此对象的类的新实例,并使用该对象相应字段的内容来完全初始化其所有字段,就像通过赋值一样;这些字段的内容本身不会被克隆。”
帕维尔米纳夫

2

来自可克隆的javadoc。

* By convention, classes that implement this interface (cloneable) should override 
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.

* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface.  Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.

因此,您可以在每个对象上调用clone,但这在大多数情况下不会给您想要的结果或例外。但是仅当您实现可克隆时才鼓励这样做。


2
不能在每个对象上调用克隆,因为它受到保护!
帕维尔米纳夫

2

恕我直言,就是这样简单:

  • #clone 不能在非克隆对象上调用,因此它不会公开
  • #clone必须由Object实现Cloneable 的子类ob调用,以获取正确类的浅表副本

子类可调用但其他类不能调用的方法的正确范围是什么?

protected

Cloneable当然,实现类的方法将使此方法公开,因此可以从其他类中调用它。


0

Clone()方法内部有一个'Cloneable实例是否可用'的检查。Java团队可能会认为这会限制对clone()方法的不当使用。clone()方法受保护,即只能由子类访问。由于object是所有子类的父类,因此,如果我们没有对“ Cloneable的实例”进行上述检查,那么所有类实际上都可以使用Clone()方法。这就是Java团队可能会考虑通过在clone()方法中检查“是否为Cloneable实例”来限制对clone()的不当使用的原因。

因此,任何实现可克隆的类都可以使用Object类的clone()方法。

同样,由于它已受到保护,因此它仅对实现可克隆接口的那些子类可用。如果我们想将其公开,则子类必须使用其自己的实现来重写此方法。


-2

是的,我遇到了同样的问题。但是我通过实现这段代码来解决

public class Side implements Cloneable {
    public Side clone() {

        Side side = null;
        try {
            side = (Side) super.clone();
        } catch (CloneNotSupportedException e) {
            System.err.println(e);
        }
        return side;
    }
}

就像之前有人说的那样。


1
CloneNotSupportedException是应检查的异常的另一个示例,应将其取消选中(即,它应扩展RuntimeException而不是Exception)。尽管类Side中的方法clone()实现了Cloneable,因此永远不会抛出CloneNotSupportedException,但Side.clone()仍必须捕获或声明该异常。这只会给clone()方法增加多余的异常处理噪音。
德里克·马哈尔

-2

好吧,Sun开发人员也只是人类,他们确实犯了巨大的错误,无法将克隆方法实现为受保护,这与他们在ArrayList中实现无效的克隆方法时一样!因此,一般而言,即使是经验丰富的Java程序员也对克隆方法存在更深的误解。

但是,我最近找到了一种快速简便的解决方案,可以复制任何对象及其所有内容,无论它是如何构建的以及包含什么内容,请在此处查看我的答案:使用Object.clone()的错误


-3

Java JDK框架再次展示了出色的思想:

可克隆的接口不包含“ public T clone();”。方法,因为它的作用更像是允许克隆其实例的属性(例如,Serializable)。

此设计没有错,因为:

  1. Object.clone()不会对您定义的类执行您想要的操作。

  2. 如果您具有Myclass实现Cloneable =>,则可以使用“ public MyClass clone()”覆盖clone()

  3. 如果您拥有MyInterface扩展Cloneable和一些实现MyInterface的MyClasses:只需定义“ public MyInterface clone();”即可。在界面中,使用MyInterface对象的每个方法都可以克隆它们,无论它们是MyClass类。


2
如果您的类碰巧被继承,则在基类实现提供安全的克隆方法之前,派生类的克隆实现将是不安全的。同样,具有没有方法/属性的接口也是一种不寻常的设计条件。该接口不强制类实现克隆。
2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.