为什么不反对克隆?

人们普遍认为,Java 中的 Cloneable接口已经坏了。有很多原因,我不会提到; 其他人已经这样做了。它也是 Java 架构师本身的位置。

因此,我的问题是: 为什么还没有被废弃?如果核心 Java 团队已经决定它已经坏了,那么他们肯定也考虑过弃用。他们反对这样做的理由是什么(在 Java8中是 还没有被废弃) ?

10150 次浏览

为什么它还没有被废弃?

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

在 JavaAPI 中保留这个东西背后的原因是什么

没有人会从 JavaAPI 中删除任何东西,因为需要向后兼容性。上一次发生这种情况是在1996/7年,AWT 事件模型在1.0和1.1之间发生了变化。

有一个 臭虫在1997年提交给 Java 程式错误资料库关于增加 clone()方法到 Cloneable,所以它将不再是无用的。最后以“不会修复”的决议结束,理由如下:

孙中山的技术审查委员会(TRC)详细考虑了这个问题 并建议不要采取任何除了改善 当前 Cloneable 接口的文档 建议案文:

现有的 Java 对象克隆 API 是有问题的 在 java.lang.Object 上保护“ clone”方法,并且有一个接口 这样做的意图是,如果一个类希望允许 其他人来克隆它,那么它应该支持克隆人 接口重写默认的受保护克隆方法 公共克隆方法。不幸的是,由于某些原因,很容易在 在时间的迷雾中,Cloneable 接口并不定义克隆 方法。

这种组合导致了相当多的混淆 声称支持克隆,但偶然忘记支持 开发人员对于如何使用 Cloneable 感到困惑 以及克隆人应该做什么。

不幸的是,向 Cloneable 中添加“ Cloneable”方法将是一个 不兼容的更改。它不会破坏二进制兼容性,但会破坏二进制兼容性 破坏源代码兼容性。轶事证据建议 实践中,有许多情况下类支持 可克隆接口,但未能提供公共克隆方法 经讨论后,储税券局一致建议我们不应修改 现有的可克隆接口,因为兼容性的影响。

另一个建议是添加一个新的界面 PubliclyCloneable 可以反映最初的预期用途 以5比2的多数票反对。 主要的担心是,这将增加更多的混乱(包括 拼写混乱!)已经混乱的图片。

储税券委员会一致建议,我们应该增加额外的 文档到现有的 Cloneable 接口 以更好地描述 如何使用和如何描述“最佳做法” 执行机构。

因此,虽然这不是直接关于 不赞成,没有使克隆“弃用”的原因是技术审查委员会决定 修改现有文档就足够了使这个接口有用。他们确实这么做了。在 Java 1.4之前,Cloneable的文档如下:

类实现 Cloneable 接口以向 方法,该方法合法地创建一个 该类实例的字段对字段的副本。

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

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

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

类实现 Cloneable 接口以向 方法,该方法合法地创建一个 该类实例的字段对字段的副本 实例上的克隆方法,该实例不实现 Cloneable 接口导致异常 CloneNotSupportedException 为 抛出。

按照约定,实现此接口的类应重写 使用公共方法进行 Object.clone (它受到保护) 获取有关重写此方法的详细信息。

请注意,此接口不包含克隆方法, 仅仅凭借事实是不可能克隆一个物体的 它实现了这个接口,即使调用了克隆方法 仔细想想,这并不能保证一定会成功。

对于“为什么 Cloneable没有被弃用?”的简短回答(或者说,为什么对于任何 X而言,X都没有被废弃) ,是因为没有太多的关注去废弃它们。

大多数最近已经废弃的东西都已经被废弃了,因为有一个具体的计划来删除它们。例如,日志管理器addPropertyChangeListenerremovePropertyChangeListener方法是 已在 JavaSE8中弃用,目的是在 JavaSE9中删除它们。(原因是它们不必要地复杂化了模块之间的相互依赖关系。)实际上,这些 API 已经是 从早期的 JDK 9中删除开发构建。(注意,类似的属性更改侦听器调用也从 Pack200中删除了; 请参见 JDK-8029806。)

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

一个更长的答案将涉及讨论进一步的问题,例如这些 API 可能会发生什么,如果它们被弃用,平台将产生什么成本或收益,以及当一个 API 被弃用时,将向开发人员传达什么。我在最近的 JavaOne 演讲 债务与折旧中探讨了这个话题。(链接处有幻灯片; 视频在这里。)事实证明,JDK 本身在弃用方法的使用上并不十分一致。它被用来表示几种不同的意思,比如,

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

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

  • 这已经过时了,使用不同的方法对您来说是个好主意(例如: java.util.Date中的许多方法)

所有这些都是不同的含义,它们的不同子集适用于不同的不推荐的事物。它们中的一些子集适用于没有被弃用的东西(但也许应该被弃用)。

CloneableObject.clone()在设计缺陷和难以正确使用的意义上是“破碎的”。但是,clone()仍然是复制数组的最佳方法,而且克隆对于复制经过仔细实现的类的实例的用处有限。消除克隆将是一个不兼容的改变,将打破很多东西。克隆操作可以用不同的方式重新实现,但它可能比 Object.clone()慢。

然而,在大多数情况下,复制建构子比克隆更可取。因此,或许将 Cloneable标记为“过时的”或“被取代的”或类似的标记是合适的。这将告诉开发人员,他们可能希望到其他地方去寻找,但这并不意味着在未来的版本中可能会删除克隆机制。不幸的是,这样的标记并不存在。

就目前情况而言,“弃用”似乎意味着最终删除——尽管事实上已经删除的弃用特性的数量已经越来越少——因此,对于克隆机制而言,弃用似乎并不合理。也许将来可以应用一种替代标记,指导开发人员使用替代机制。

更新

我给 漏洞报告增加了一些额外的历史。Frank Yellin 是早期的 JVM 实现者,也是 JVM 规范的合著者,他针对 另一个答案中引用的 TRC 建议中的“迷失在时间迷雾中”的评论做出了一些评论。我在这里引用了相关的部分; 完整的信息在 bug 报告中。

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

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

简而言之。Cloneable 的目的不是指明您拥有一个公共 clone()方法。它表明您愿意使用 Object.clone ()进行克隆,而是由实现来决定是否公开  ()。