为什么Cloneable不被弃用?

问题

众所周知,Java中的Cloneable接口被破坏了。这有很多原因,我不会提及; others已经做到了。它也是Java architects自己的立场。

因此,我的问题是:为什么还没有被弃用?如果核心Java团队已经确定它已被破坏,那么他们也必须考虑弃用。他们这样做的原因是什么(在Java 8中它是still not deprecated)?


#1 热门回答(117 赞)

在1997年提交的abugJava Bug Database,并且在Cloneable中添加了clone()方法,因此它将不再是无用的。它以"不会修复"的决议结束,理由如下:

Sun的技术审查委员会(TRC)详细考虑了这个问题,并建议不要采取任何行动,而不是改进当前Cloneable接口的文档。以下是建议的全文:现有的Java对象克隆API存在问题。 java.lang.Object上有一个受保护的"clone"方法,并且有一个接口java.lang.Cloneable。目的是如果一个类想要允许其他人克隆它,那么它应该支持Cloneable接口并使用公共克隆方法覆盖默认的受保护克隆方法。不幸的是,由于在时间迷雾中方便丢失的原因,Cloneable接口没有定义克隆方法。这种组合导致了相当多的混乱。有些类声称支持Cloneable,但不小心忘记支持克隆方法。开发人员对Cloneable应该如何工作以及克隆应该做什么感到困惑。不幸的是,向Cloneable添加"克隆"方法将是一种不兼容的更改。它不会破坏二进制兼容性,但会破坏源兼容性。轶事证据表明,在实践中,有许多类支持Cloneable接口,但无法提供公共克隆方法。经过讨论,TRC一致建议我们不要修改现有的Cloneable接口,因为兼容性影响。另一个提议是添加一个新接口java.lang.PubliclyCloneable以反映Cloneable的原始预期目的。以5比2多数,TRC建议不要这样做。主要担心的是,这会给已经混乱的图片增加更多的混淆(包括拼写错误!)。 TRC一致建议我们应该在现有Cloneable接口上添加其他文档,以更好地描述它的使用方式,并为实现者描述"最佳实践"。

因此,虽然这不是直接关于不推荐的,但是不使Cloneable"弃用"的原因是,技术评审委员会决定修改现有文档的4669681446足以使这个界面变得有用了。所以他们做到了。直到Java 1.4,Cloneable记录如下:

一个类实现了Cloneable接口,以向Object.clone()方法指示该方法合法地为该类的实例创建一个field-for-field副本。尝试克隆未实现Cloneable接口的实例会导致抛出CloneNotSupportedException异常。 Cloneable接口声明没有方法。

从Java 1.4(2002年2月发布)到当前版本(Java 8),它看起来像这样:

一个类实现了Cloneable接口,以向Object.clone()方法指示该方法合法地为该类的实例创建一个field-for-field副本。在未实现Cloneable接口的实例上调用Object的clone方法会导致抛出异常CloneNotSupportedException。按照惯例,实现此接口的类应使用公共方法覆盖Object.clone(受保护)。有关重写此方法的详细信息,请参阅Object.clone()。请注意,此接口不包含克隆方法。因此,仅仅通过实现该接口的事实来克隆对象是不可能的。即使反射调用clone方法,也无法保证它会成功。


#2 热门回答(63 赞)

简单回答"为什么不是Cloneable贬低?" (或者实际上,为什么not1111428949已经过去,对于任何X而言)是因为对它们的弃用并没有太多关注。

最近弃用的大部分内容都已弃用,因为有一个特定的计划要删除它们。例如,addPropertyChangeListenerremovePropertyChangeListener方法LogManagerwevedeprecated in Java SE 8,目的是在Java SE 9中删除它们。(原因是它们不必要地复杂了模块的相互依赖性。)实际上,这些API已经被开发了。 (请注意,类似的属性更改侦听器调用也从Pack200中删除;请参阅JDK-8029806。)

对于CloneableObject.clone(),没有类似的计划。

更长的答案将涉及讨论进一步的问题,例如人们可能期望在这些API上发生的事情,如果平台被弃用会产生什么样的成本或收益,以及在API被弃用时与开发人员沟通的内容。我在最近的JavaOne演讲Debt and Deprecation中探讨了这个主题。 (在该链接上可用的幻灯片; video here。)事实证明,JDK本身在使用弃用方面并不十分一致。它被用来表示几种不同的东西,包括例如,

  • 这很危险,你应该知道使用它的风险(例如:Thread.stop(),Thread.resume()和Thread.suspend())。
  • 这将在以后的版本中删除
  • 这已经过时了,你最好使用不同的东西(例如:java.util.Date中的许多方法)

所有这些都是不同的含义,它们的不同子集适用于不推荐使用的不同内容。它们的一些子集适用于未弃用的东西(但可能应该弃用)。

CloneableObject.clone()在它们有设计缺陷并且难以正确使用的意义上是"破碎的"。但是,clone()仍然是复制数组的最佳方法,而克隆对于精心实现的类实例的副本有一些有限的用处。删除克隆将是一个不兼容的变化,会破坏很多东西。克隆操作可以以不同的方式重新实现,但它可能比Object.clone()慢。

但是,对于大多数情况,复制构造函数优于克隆。所以也许将Cloneable标记为"过时"或"被取代"或类似的东西是合适的。这将告诉开发人员他们可能想要寻找其他地方,但它不会表明在将来的版本中可能会删除克隆机制。不幸的是,不存在这样的标记。

事实上,"弃用"似乎意味着最终被删除 - 尽管事实上已经删除了少量被弃用的功能 - 因此克隆机制似乎不值得推荐。也许将来可以应用替代标记,指导开发人员使用替代机制。
更新
我已经为bug report添加了一些额外的历史记录。早期的JVM实现者和JVM规范的共同作者Frank Yellin在other answer引用的TRC建议中回应了"迷失在时间之中"的评论。我在这里引用了相关部分;完整的消息在错误报告中。

Cloneable没有方法与Serializable没有相同的原因。 Cloneable表示类的属性,而不是具体说明该类支持的方法。在反射之前,我们需要一个本机方法来制作一个Object的浅表副本。因此,Object.clone()诞生了。很明显,许多类都希望覆盖此方法,并且不是每个类都希望克隆。因此Cloneable的诞生是为了表明程序员的意图。所以,简而言之。 Cloneable的目的不是表明你有一个公共clone()方法。这表明你愿意使用Object.clone()进行克隆,并且由实现决定是否公开clone()。


#3 热门回答(-1 赞)

为什么它不被弃用了?

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

在Java API中保留此内容的原因是什么?

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