为什么可克隆未弃用?

2022-08-31 07:50:35

通常理解为Java中的接口已损坏。造成这种情况的原因有很多,我就不多说了。其他人已经做到了。这也是Java架构师自己的立场。Cloneable

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


答案 1

1997 年向 Java Bug 数据库提交了一个关于添加方法的错误,所以它不再是无用的。它以决议“不会修复”结束,理由如下:clone()Cloneable

Sun的技术审查委员会(TRC)详细考虑了这个问题,并建议除了改进当前可克隆接口的文档之外,不要采取任何行动。以下是建议的全文:

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

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

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

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

TRC一致建议,我们应该向现有的可克隆接口添加其他文档,以更好地描述它的使用方式,并为实现者描述“最佳实践”。

因此,尽管这与弃用并不直接相关,但未使Cloneable“弃用”的原因是Technical Review Comitee认为修改现有文档足以使此接口有用。所以他们做到了。在Java 1.4之前,有如下文档记录:Cloneable

类实现 Cloneable 接口,以向 Object.clone() 方法指示该方法对该类的实例进行逐字段复制是合法的。

尝试克隆未实现可克隆接口的实例会导致引发异常 CloneNotSupportedException。

可克隆接口不声明任何方法。

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

类实现 Cloneable 接口,以向 Object.clone() 方法指示该方法对该类的实例进行字段对字段复制是合法的。在未实现 Cloneable 接口的实例上调用 Object 的 clone 方法会导致引发异常 CloneNotSupportedException。

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

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


答案 2

对“为什么没有被弃用?(或者实际上,为什么不被弃用,对于任何)是没有太多的注意力来弃用它们。CloneableXX

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

不存在与 for 和 类似的计划。CloneableObject.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没有方法,原因与Sscribializable没有的原因相同。Cloneable 指示类的属性,而不是专门说明该类支持的方法。

在反射之前,我们需要一个本机方法来制作对象的浅层副本。因此Object.clone()诞生了。同样清楚的是,许多类都希望重写此方法,并且并非每个类都希望被克隆。因此,Cloneable的诞生是为了表明程序员的意图。

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


推荐