为什么克隆不被弃用?

人们普遍认为Java中的Cloneable接口已经被破坏了。 这个原因有很多,我不会提到; 其他人已经做到了。 这也是Java架构师自己的立场。

所以我的问题是:为什么还没有被弃用呢? 如果核心Java团队已经决定它被破坏,那么他们也必须考虑弃用。 他们的反对理由是什么(在Java 8中,它还没有被废弃 )?

在1997年提交给Java Bug Database的一个bug是关于将clone()方法添加到Cloneable ,所以它不再是无用的。 它以“不能修复”的决议结案,理由如下:

Sun的技术评审委员会(TRC)详细考虑了这个问题,并build议不要采取任何行动,除非改进当前Cloneable界面的文档 。 以下是build议的全文:

现有的Java对象克隆API是有问题的。 在java.lang.Object上有一个受保护的“克隆”方法,并且有一个接口java.lang.Cloneable。 其意图是,如果一个类想要允许其他人克隆它,那么它应该支持Cloneable接口并用公共克隆方法覆盖默认的受保护的克隆方法。 不幸的是,由于时间的stream逝迷失方便,Cloneable接口没有定义一个克隆方法。

这种组合导致相当多的混淆。 有些类宣称支持Cloneable,但不小心忘记支持克隆方法。 开发人员对Cloneable如何工作以及克隆应该如何工作感到困惑。

不幸的是,向克隆添加“克隆”方法将是一个不兼容的变化。 它不会破坏二进制兼容性,但会破坏源代码兼容性。 传闻证据表明,在实践中,有许多类支持Cloneable接口,但是没有提供公共克隆方法。 经过讨论,TRC一致build议我们不要修改现有的Cloneable接口,因为兼容性的影响。

一个替代scheme是添加一个新的接口java.lang.PubliclyCloneable来反映Cloneable原来的预期目的。 TRC以5至2的多数build议反对这一点。 主要担心的是,这会给已经混淆的图片添加更多的混淆(包括拼写混淆!)。

TRC一致build议,我们应该添加额外的文档到现有的Cloneable接口,以更好地描述如何使用它,并描述实现者的“最佳实践”。

因此,虽然这不是直接被弃用的 ,但是没有制作Cloneable“弃用”的原因是Technical Review Comitee认为修改现有的文档足以使这个接口有用。 所以他们做到了。 在Java 1.4之前, Cloneable被logging如下:

一个类实现了Cloneable接口,以向Object.clone()方法表明该方法是合法的,为该类的实例创build一个field-for-field副本。

尝试克隆不实现Cloneable接口的实例会导致引发exceptionCloneNotSupportedException。

Cloneable接口声明没有方法。

从Java 1.4(2002年2月发布)到现在的版本(Java 8)之后,它看起来像这样:

一个类实现了Cloneable接口,以向Object.clone()方法表明该方法是合法的,为该类的实例创build一个field-for-field副本。 在没有实现Cloneable接口的实例上调用Object的clone方法会导致引发exceptionCloneNotSupportedException。

按照惯例,实现这个接口的类应该用public方法重写Object.clone(被保护)。 有关覆盖此方法的详细信息,请参阅Object.clone()。

请注意,此接口不包含克隆方法。 因此,仅仅凭借实现这个接口的事实来克隆一个对象是不可能的。 即使克隆方法是reflection性调用,也不能保证它会成功。

“为什么不Cloneable不推荐”的简短答案? (或者说,为什么不推荐使用X ,因为任何X )都没有太多的注意力来贬低它们。

大多数最近被弃用的东西都被弃用,因为有一个特定的计划去除它们。 例如, LogManager中的addPropertyChangeListenerremovePropertyChangeListener方法在Java SE 8中已被废弃,目的是在Java SE 9中删除它们(原因是它们不必要地复杂了模块相互依赖性)。事实上,这些API已经从早期的JDK 9发展build设。 (请注意,类似的属性更改侦听器调用也从Pack200删除;请参阅JDK-8029806 。)

对于CloneableObject.clone()不存在类似的计划。

较长的答案将涉及到讨论进一步的问题,比如这些API可能会发生什么事情,如果不推荐使用这些平台,那么会产生哪些成本或收益,以及当API被弃用时传递给开发人员的是什么。 我在最近的JavaOne谈话“ 债务与弃用”中探讨了这个话题。 (幻灯片可在此链接; video在这里 。)事实certificate,JDK本身在使用弃用方面并不一致。 它被用来表示几个不同的东西,例如,

  • 这是危险的,你应该知道使用它的风险(例如: Thread.stop()Thread.resume()Thread.suspend() )。

  • 这将在未来的版本中被删除

  • 这已经过时了,最好使用不同的东西(例如: java.util.Date许多方法)

所有这些都是不同的含义,不同的子集适用于不赞成使用的不同事物。 它们的一些子集适用于不被弃用的东西(但可能应该被弃用)。

CloneableObject.clone()在devise缺陷和难以正确使用的意义上是“破碎的”。 然而, clone()仍然是复制数组的最佳方式,而克隆只能有限地复制仔细实现的类的实例。 删除克隆将是一个不兼容的变化,将打破许多事情。 克隆操作可以用不同的方式重新实现,但是可能比Object.clone()慢。

但是,对于大多数情况来说,复制构造函数比克隆更可取。 因此,将Cloneable标记为“过时”或“被取代”或类似的内容是合适的。 这会告诉开发者,他们可能想看看其他地方,但这并不表示克隆机制可能会在未来版本中被删除。 不幸的是,不存在这样的标记。

就目前情况而言,“贬低”似乎意味着最终将被删除,尽pipe事实上已经删除了一小部分已弃用的function – 而且对于克隆机制来说,这样做似乎并不合理。 也许在将来,可以应用替代标记来指导开发者使用替代机制。

UPDATE

我已经添加了一些额外的历史错误报告 。 作为JVM规范的早期JVM实现者和共同作者,Frank Yellin针对另一个答案中引用的TRCbuild议中的“迷失时间”评论发表了一些评论。 我在这里引用了相关部分。 完整的信息在错误报告中。

因为Serializable没有,所以Cloneable没有方法。 Cloneable表示类的一个属性,而不是具体说明类支持的方法。

在反思之前,我们需要一个本地方法来创build一个对象的浅拷贝。 因此Object.clone()诞生了。 也很清楚,许多课程都想要重写这个方法,而且并不是每一个课程都想被克隆。 因此Cloneable诞生了,表明了程序员的意图。

所以,总之。 Cloneable的目的并不是说你有一个公共的clone()方法。 这意味着你愿意使用Object.clone()进行克隆,并且由实现决定是否公开clone()。

为什么它不被弃用呢?

因为JCP并不适合这样做,而且可能永远不会这样做。 问他们。 你在错误的地方问。

在Java API中保留这个东西的原因是什么?

由于向后兼容性的要求,没有人会从Java API中删除任何东西。 最后一次发生的是1996/7年度AWT事件模型在1.0和1.1之间的变化。