如何正确覆盖克隆方法?

Cug*_*uga 109 java clone cloneable cloning

我需要在我的一个没有超类的对象中实现深度克隆.

处理CloneNotSupportedException超类抛出的检查的最佳方法是什么(是Object)?

一位同事建议我按以下方式处理:

@Override
public MyObject clone()
{
    MyObject foo;
    try
    {
        foo = (MyObject) super.clone();
    }
    catch (CloneNotSupportedException e)
    {
        throw new Error();
    }

    // Deep clone member fields here

    return foo;
}
Run Code Online (Sandbox Code Playgroud)

这对我来说似乎是一个很好的解决方案,但我想把它扔到StackOverflow社区,看看是否还有其他任何我可以包含的见解.谢谢!

pol*_*nts 121

你绝对要用clone吗?大多数人都认为Java已clone被打破.

设计中的Josh Bloch - 复制构造函数与克隆

如果你已经在我的书中阅读了关于克隆的项目,特别是如果你在这些内容之间阅读,那么你会知道我认为它clone已经深受打击.[...]这是一个Cloneable被打破的耻辱,但它发生了.

您可以在他的书" Effective Java 2nd Edition,Item 11:clone明智地覆盖"中阅读有关该主题的更多讨论.他建议改为使用复制构造函数或复制工厂.

他接着写了一些页面,说明如果你觉得必须,你应该如何实施clone.但他以此结束了:

所有这些复杂性真的有必要吗?很少.如果扩展一个实现的类,Cloneable除了实现一个行为良好的clone方法之外别无选择.否则,您最好提供替代的对象复制方法,或者根本不提供该功能.

重点是他,而不是我的.


既然你明确表示你别无选择,只能实施clone,这就是你在这种情况下可以做的事情:确保这一点MyObject extends java.lang.Object implements java.lang.Cloneable.如果是这样,那么你可以保证你永远不会抓到一个CloneNotSupportedException.AssertionError像某些人所说的那样投掷似乎是合理的,但你也可以添加一个注释来解释为什么在这种特殊情况下永远不会输入catch块.


或者,正如其他人还建议,可以或许实现clone,而无需调用super.clone.

  • 如果一个类及其所有超类在其克隆方法中调用`super.clone()`,则子类通常只需要覆盖`clone()`,如果它添加了需要克隆其内容的新字段.如果任何超类使用`new`而不是`super.clone()`,则所有子类都必须覆盖`clone()`,无论它们是否添加任何新字段. (5认同)
  • 不幸的是,该项目已经是使用克隆方法编写的,否则我绝对不会使用它。我完全同意你的观点,Java对clone的实现是fakakta。 (2认同)

Aar*_*lla 55

有时,实现复制构造函数更简单:

public MyObject (MyObject toClone) {
}
Run Code Online (Sandbox Code Playgroud)

它可以节省您处理的麻烦CloneNotSupportedException,使用final字段并且您不必担心要返回的类型.


Chr*_*ung 11

代码的工作方式非常接近于编写它的"规范"方式.不过,我会把它扔进去AssertionError.它表示永远不应该到达那条线.

catch (CloneNotSupportedException e) {
    throw new AssertionError(e);
}
Run Code Online (Sandbox Code Playgroud)


And*_*yle 9

CloneNotSupportedException将抛出两种情况:

  1. 被克隆的类没有实现Cloneable(假设实际克隆最终遵循Object克隆方法).如果您在实现中编写此方法的类Cloneable,则永远不会发生这种情况(因为任何子类都将适当地继承它).
  2. 实现显式抛出异常 - 这是在超类时防止子类中的克隆性的推荐方法Cloneable.

后一种情况不能在类中发生(因为您直接调用在父类的方法try块,即使从子类调用调用super.clone())和前不应该因为你的类显然应该实现Cloneable.

基本上,您应该确定记录错误,但在这个特定的实例中,只有当您搞砸了类的定义时才会发生.因此将其视为NullPointerException(或类似的)检查版本- 如果您的代码功能正常,它将永远不会被抛出.


在其他情况下,你将需要为这种可能性做好准备-没有保证,一个给定的对象可复制的,所以捕捉异常时,应采取适当的行动取决于这个条件(继续使用现有的对象,采取另一种克隆策略例如,serialize-deserialize,IllegalParameterException如果你的方法需要可克隆的参数等,则抛出一个等等.

编辑:尽管总体来说,我要指出的是,是的,clone()真的是很难正确地实施和困难呼叫者知道的返回值是否是他们想要的东西,加倍所以当你考虑深VS浅克隆.通常更好的方法是完全避免整个事情并使用其他机制.


Mic*_*ber 5

使用序列化进行深层复制.这不是最快的解决方案,但它不依赖于类型.