142

众所周知,CloneableJava 中的接口已损坏。原因有很多,我就不说了;其他人已经这样做了。这也是Java架构师自己的立场。

因此,我的问题是:为什么还没有被弃用?如果核心 Java 团队决定它被破坏了,那么他们一定也考虑过弃用。他们反对这样做的原因是什么(在 Java 8 中它仍然没有被弃用)?

4

3 回答 3

121

1997 年向 Java Bug Database提交了一个关于添加clone()方法的错误Cloneable,因此它不再无用。它以“不会修复”的决议关闭,理由如下:

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

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

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

不幸的是,向 Cloneable 添加“克隆”方法将是不兼容的更改。它不会破坏二进制兼容性,但会破坏源代码兼容性。轶事证据表明,在实践中存在许多类支持 Cloneable 接口但未能提供公共克隆方法的情况。经过讨论,TRC一致建议不要修改现有的Cloneable接口,因为会影响兼容性。

另一种建议是添加一个新接口 java.lang.PubliclyCloneable 以反映 Cloneable 的原始预期用途。TRC 以 5 比 2 的多数反对这样做。主要担心的是,这会给已经混乱的图片增加更多的混乱(包括拼写混乱!)。

TRC 一致建议我们应该在现有的 Cloneable 接口中添加额外的文档,以更好地描述它的使用方式,并为实现者描述“最佳实践”。

因此,尽管这与 deprecated 没有直接关系,但使 Cloneable “弃用”的原因是 Technical Review Comitee 认为修改现有文档足以使该接口变得有用。他们就这样做了。在 Java 1.4 之前,Cloneable记录如下:

一个类实现了 Cloneable 接口,以向 Object.clone() 方法指示该方法制作该类实例的逐个字段副本是合法的。

尝试克隆未实现 Cloneable 接口的实例会导致抛出异常 CloneNotSupportedException。

接口 Cloneable 没有声明任何方法。

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

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

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

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

于 2014-10-16T11:31:07.050 回答
64

Cloneable“为什么不被弃用?”的简短回答 (或者实际上,为什么不X被弃用,对于任何X)是没有太多关注来弃用它们。

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

Cloneable对于和不存在类似的计划Object.clone()

更长的答案将涉及讨论进一步的问题,例如人们可能期望这些 API 会发生什么,如果它们被弃用,平台会产生哪些成本或收益,以及当 API 被弃用时与开发人员沟通的内容。我在最近的 JavaOne 演讲“债务与弃用”中探讨了这个主题。(该链接上提供的幻灯片;视频在这里。)事实证明,JDK 本身在其弃用的使用上并不是很一致。它被用来表示几种不同的事物,例如,

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

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

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

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

Cloneable并且Object.clone()在某种意义上是“损坏的”,因为它们存在设计缺陷并且难以正确使用。但是,clone()它仍然是复制数组的最佳方式,并且克隆对于复制经过仔细实现的类的实例的用处有限。删除克隆将是一个不兼容的更改,会破坏很多东西。可以以不同的方式重新实现克隆操作,但它可能会比Object.clone().

然而,对于大多数事情来说,复制构造函数比克隆更可取。因此,也许将其标记Cloneable为“过时”或“已取代”或类似的东西是合适的。这将告诉开发人员他们可能想要寻找其他地方,但这并不意味着克隆机制可能会在未来的版本中被删除。不幸的是,不存在这样的标记。

就目前情况而言,“弃用”似乎意味着最终删除——尽管事实上已经删除了极少数已弃用的功能——因此克隆机制似乎没有必要弃用。也许将来可以应用替代标记,指导开发人员使用替代机制。

更新

我在错误报告中添加了一些额外的历史记录。早期的 JVM 实现者和 JVM 规范的合著者 Frank Yellin 发表了一些评论,以回应另一个答案中引用的 TRC 建议中的“迷失在时间的迷雾中”的评论。我在这里引用了相关部分;完整的消息在错误报告中。

Cloneable 没有方法,原因与 Serializable 没有方法相同。Cloneable 表示类的属性,而不是具体说明类支持的方法。

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

所以,简而言之。Cloneable 的目的不是表明您有一个公共的 clone() 方法。这是为了表明您愿意使用 Object.clone() 进行克隆,并且由实现决定是否将 clone() 公开。

于 2014-10-16T23:30:52.247 回答
-2

为什么它还没有被弃用?

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

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

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

于 2014-10-16T08:49:53.303 回答