【问题标题】:Why is Cloneable not deprecated?为什么 Cloneable 没有被弃用?
【发布时间】:2014-12-11 11:57:38
【问题描述】:

一般的理解是Java中的Cloneable接口坏了。原因有很多,我就不说了; others 已经做到了。也是Java architects自己的位置。

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

【问题讨论】:

  • 这个问题不是“主要基于意见的”,因为许多人显然认为有权判断。那些仅仅对原因有意见的人根本没有资格回答。但是,确实,您在这里获得权威答案的机会微乎其微。你的问题也不是关于你有一个可解决的问题,所以它至少是边缘题外话。
  • @MarkoTopolnik 我同意世界上有些人可以提供权威的答案,但我不认为这是我们在这里应用的测试。关闭原因指出“这个问题的答案将倾向于几乎完全基于意见”。我怀疑这里会是这样,除非我们非常幸运。
  • 这里是从 Oracle 中弃用的“如何以及何时”... (docs.oracle.com/javase/6/docs/technotes/guides/javadoc/…) 可克隆接口可能属于“有缺陷或效率极低”的情况,但它对意见非常开放。跨度>
  • @Duncan 我仍然认为根据我对回答者缺乏纪律的假设对问题做出判断是不公平的 部分。如果用户不知道被问及的原因,他/她无权滥用回答工具来表达他/她对此事的看法。
  • @lexicore 是的,完全正确 --- 你可以打赌他们已经彻底考虑了这个选项,并且暗示必须有 strong 理由不弃用它。他们自己对Cloneable 的批评广为人知。

标签: java java-8 deprecated cloneable


【解决方案1】:

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

最近已弃用的大多数内容都已弃用,因为有特定的计划将其删除。例如,LogManageraddPropertyChangeListenerremovePropertyChangeListener 方法是 deprecated in Java SE 8,目的是在 Java SE 9 中删除它们。(原因是它们不必要地复杂了模块相互依赖关系。)事实上,这些 API 已经被removed from early JDK 9 开发版本。 (请注意,类似的属性更改侦听器调用也已从 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 中添加了一些额外的历史记录。 Frank Yellin 是早期的 JVM 实现者和 JVM 规范的合著者,针对other answer 中引用的 TRC 建议中的“迷失在时间的迷雾中”评论做出了一些 cmets。我在这里引用了相关部分;完整的消息在错误报告中。

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

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

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

【讨论】:

  • 您在这里有一个很好的答案,先生。我特别喜欢你不只是因为其他人都想把Object.clone()扔进火里,而是你愿意推理并提出它的好处。
  • 但是,clone() 仍然是复制数组的最佳方法,并且克隆在复制经过仔细实现的类实例方面的用处有限。修复 6428387 的印象是,所有代码路径(克隆、新/arrayCopy、Arrays.copyOf)都导致相同的内在函数。最近有什么变化吗?
  • @bestsss 我不认为array.clone() 一定比任何替代方案都快。从 API 的角度来看,这是复制数组的最简洁的方法。 Arrays.copyOf(array, newlen) 很接近,但它需要一个长度参数,如果你不改变长度,这是多余的。
  • @Holger 是的,据我们所知,这是自 1.1 以来第一次实际删除 API。还要注意,即使我们同意Thread.suspend()Thread.stop()(无参数)是危险的,它们也可能不会被删除——或者更改为无条件抛出异常——因为人们实际使用它们!大概他们愿意承担风险。属性更改侦听器的缓解因素之一是它们很少使用,因此删除它们的影响很小。
  • @Holger 从概念上讲,java.beans 可以独立于java.desktop,因为 bean 只是属性的库 API。不幸的是,如果您深入研究 bean API,就会发现很多对 AWT 的依赖。实施还有更多。当然,有可能解脱它们,但这样做似乎比从 bean 中分离日志记录要多得多。整个模块化工作就是为了解决这个问题。毫无疑问可以做更多,但 Jigsaw 将需要更长的时间。
【解决方案2】:

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

Sun 的技术审查委员会 (TRC) 详细考虑了这个问题 并建议不要采取除了改进 当前 Cloneable 接口的文档。这是完整的 推荐文字:

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

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

不幸的是,向 Cloneable 添加“克隆”方法将是 不兼容的变化。它不会破坏二进制兼容性,但会 打破源兼容性。轶事证据表明,在 实践中有许多类支持 可克隆接口,但未能提供公共克隆方法。后 讨论中,TRC 一致建议我们不应该修改 现有的 Cloneable 接口,因为兼容性影响。

另一种建议是添加一个新界面 java.lang.PubliclyCloneable 以反映最初的预期目的 可克隆的。 TRC 以 5 比 2 的多数反对这样做。 主要的担忧是这会增加更多的混乱(包括 拼写混乱!)到已经混乱的图片。

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

因此,虽然这与已弃用没有直接关系,但没有将 Cloneable “弃用”的原因是技术审查委员会认为修改现有文档就足够了使这个界面有用。他们就这样做了。在 Java 1.4 之前,Cloneable 的文档如下:

一个类实现了 Cloneable 接口来指示 Object.clone() 方法,该方法创建一个合法的 该类实例的逐场副本。

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

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

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

一个类实现了 Cloneable 接口来指示 Object.clone() 方法,该方法创建一个合法的 该类实例的逐场副本。调用对象 未实现 Cloneable 的实例上的 clone 方法 接口导致异常 CloneNotSupportedException 是 抛出。

按照惯例,实现这个接口的类应该重写 使用公共方法的 Object.clone(受保护)。看 Object.clone() 以获取有关覆盖此方法的详细信息。

请注意,此接口不包含克隆方法。所以, 不可能仅仅因为这个事实而克隆一个对象 它实现了这个接口。即使调用了克隆方法 仔细想想,并不能保证它会成功。

【讨论】:

  • 你知道为什么clone 方法首先出现在Object 中吗?
  • @njzk2 这是该机制的重要组成部分——它是执行低级、语言外魔术的方法,逐位复制对象图像。
  • @Unheilig 这种魔法是复制构造函数无法复制的:Object#clone() 生成与原始类相同的类的实例,而在编译时该类不为人所知。
  • @AVolpe:这不能解决“类支持 Cloneable 接口但无法提供公共克隆方法”的答案中提到的源不兼容问题。特别是,提供非公共克隆方法的类会被破坏。
  • 美好的历史。这是错误数据库的direct link。我在那里添加了更多历史记录,并在my answer 中引用了其中的部分内容。
【解决方案3】:

为什么它还没有被弃用?

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

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

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

【讨论】:

  • 他们确实(有效地)删除了Thread.stop(Throwable),方法是将其合约更改为始终将UnsupportedOperationException 抛出给调用者(而不是目标线程!)。
  • 同时发生了什么?在 Java 8 中删除了 Thread.stop(Throwable) 的功能。无论如何,“问他们”的无条件建议是错误的,因为今天首席 Java 架构师本人是 Stack Overflow 上的活跃成员。除了与 Streams 相关的问题,他只是懒得回答任何问题。
  • 此外,OP 的问题不是关于 removal,而是关于 deprecation,并且显然一直在发生弃用。
  • @EJP 我不是在问是否会从 Java API 中删除 Cloneable。我在问为什么它不会被贬低。我认为这是一个完美的提问场所。
  • @VaheHarutyunyan 感谢您的支持,但我不是 Java 架构师。我是 Oracle JDK 组的工程师,负责维护这些东西。
猜你喜欢
  • 2020-03-17
  • 1970-01-01
  • 2016-03-11
  • 2018-05-08
  • 1970-01-01
  • 1970-01-01
  • 2015-11-22
  • 2019-03-09
  • 2012-02-01
相关资源
最近更新 更多