如何正确重写克隆方法?
我需要在我的一个没有超类的对象中实现深度克隆。
处理超类(即Object
)抛出的已检查CloneNotSupportedException
的最佳方法是什么?
一位同事建议我按以下方式处理它:
@Override
public MyObject clone()
{
MyObject foo;
try
{
foo = (MyObject) super.clone();
}
catch (CloneNotSupportedException e)
{
throw new Error();
}
// Deep clone member fields here
return foo;
}
这对我来说似乎是一个很好的解决方案,但我想将其扔给 StackOverflow 社区,看看是否还有其他我可以包含的见解。谢谢!
I need to implement a deep clone in one of my objects which has no superclass.
What is the best way to handle the checked CloneNotSupportedException
thrown by the superclass (which is Object
)?
A coworker advised me to handle it the following way:
@Override
public MyObject clone()
{
MyObject foo;
try
{
foo = (MyObject) super.clone();
}
catch (CloneNotSupportedException e)
{
throw new Error();
}
// Deep clone member fields here
return foo;
}
This seems like a good solution to me, but I wanted to throw it out to the StackOverflow community to see if there are any other insights I can include. Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
您绝对必须使用
克隆
吗?大多数人都认为 Java 的克隆
已被破坏。Josh Bloch 谈设计 - 复制构造函数与克隆
您可以在他的书 Effective Java 2nd Edition, Item 11: Override
clone
明智地中阅读有关该主题的更多讨论。他建议改用复制构造函数或复制工厂。他接着写了一页又一页的内容来说明如果您认为必须的话,应该如何实现
克隆
。但他最后这样说:重点是他的,不是我的。
既然您明确表示除了实现克隆之外别无选择,在这种情况下您可以执行以下操作:确保 MyObject 扩展 java.lang.Object 实现 java.lang.Cloneable 。如果是这种情况,那么您可以保证您永远不会捕获
CloneNotSupportedException
。正如一些人建议的那样抛出AssertionError
似乎是合理的,但您也可以添加注释来解释为什么 在这种特殊情况下永远不会输入 catch 块。或者,正如其他人也建议的那样,您也许可以在不调用 super.clone 的情况下实现克隆。
Do you absolutely have to use
clone
? Most people agree that Java'sclone
is broken.Josh Bloch on Design - Copy Constructor versus Cloning
You may read more discussion on the topic in his book Effective Java 2nd Edition, Item 11: Override
clone
judiciously. He recommends instead to use a copy constructor or copy factory.He went on to write pages of pages on how, if you feel you must, you should implement
clone
. But he closed with this:The emphasis was his, not mine.
Since you made it clear that you have little choice but to implement
clone
, here's what you can do in this case: make sure thatMyObject extends java.lang.Object implements java.lang.Cloneable
. If that's the case, then you can guarantee that you will NEVER catch aCloneNotSupportedException
. ThrowingAssertionError
as some have suggested seems reasonable, but you can also add a comment that explains why the catch block will never be entered in this particular case.Alternatively, as others have also suggested, you can perhaps implement
clone
without callingsuper.clone
.有时实现复制构造函数更简单:
它为您省去了处理 CloneNotSupportedException 的麻烦,可与 Final 字段一起使用,并且您不必担心要返回的类型。
Sometimes it's more simple to implement a copy constructor:
It saves you the trouble of handling
CloneNotSupportedException
, works withfinal
fields and you don't have to worry about the type to return.您的代码的工作方式非常接近“规范”的编写方式。不过,我会在 catch 中抛出一个
AssertionError
。它表明永远不应该到达该线。The way your code works is pretty close to the "canonical" way to write it. I'd throw an
AssertionError
within the catch, though. It signals that that line should never be reached.有两种情况会抛出
CloneNotSupportedException
:Cloneable
(假设实际克隆最终遵循Object
> 的克隆方法)。如果您编写此方法的类实现了Cloneable
,则这种情况永远不会发生(因为任何子类都会适当地继承它)。Cloneable
时,这是防止子类中可克隆性的推荐方法。后一种情况不能发生在您的类中(因为您直接在
try
块中调用超类的方法,即使是从调用super.clone()
的子类调用的)而前者不应该,因为你的类显然应该实现Cloneable
。基本上,您应该肯定记录错误,但在这个特定的实例中,只有当您搞乱类的定义时才会发生这种情况。因此,将其视为 NullPointerException 的检查版本(或类似版本) - 如果您的代码正常运行,则永远不会抛出该异常。
在其他情况下,您需要为这种可能性做好准备 - 不能保证给定的对象是可克隆的,因此当捕获异常时,您应该根据这种情况采取适当的操作(继续使用现有的对象)对象,采取替代克隆策略,例如序列化-反序列化,如果您的方法需要可克隆的参数,则抛出IllegalParameterException,等等)。
编辑:虽然总的来说我应该指出,是的,
clone()
确实很难正确实现,并且调用者很难知道返回值是否是他们想要的,当你考虑深克隆和浅克隆时更是如此。通常最好完全避免整个事情并使用另一种机制。There are two cases in which the
CloneNotSupportedException
will be thrown:Cloneable
(assuming that the actual cloning eventually defers toObject
's clone method). If the class you are writing this method in implementsCloneable
, this will never happen (since any sub-classes will inherit it appropriately).Cloneable
.The latter case cannot occur in your class (as you're directly calling the superclass' method in the
try
block, even if invoked from a subclass callingsuper.clone()
) and the former should not since your class clearly should implementCloneable
.Basically, you should log the error for sure, but in this particular instance it will only happen if you mess up your class' definition. Thus treat it like a checked version of
NullPointerException
(or similar) - it will never be thrown if your code is functional.In other situations you would need to be prepared for this eventuality - there is no guarantee that a given object is cloneable, so when catching the exception you should take appropriate action depending on this condition (continue with the existing object, take an alternative cloning strategy e.g. serialize-deserialize, throw an
IllegalParameterException
if your method requires the parameter by cloneable, etc. etc.).Edit: Though overall I should point out that yes,
clone()
really is difficult to implement correctly and difficult for callers to know whether the return value will be what they want, doubly so when you consider deep vs shallow clones. It's often better just to avoid the whole thing entirely and use another mechanism.使用序列化来制作深层复制。这不是最快的解决方案,但它不依赖于类型。
Use serialization to make deep copies. This is not the quickest solution but it does not depend on the type.
您可以像这样实现受保护的复制构造函数:
You can implement protected copy constructors like so:
尽管这里的大多数答案都是有效的,但我需要告诉您的是,您的解决方案也是实际的 Java API 开发人员的做法。 (Josh Bloch 或 Neal Gafter)
以下是 openJDK ArrayList 类的摘录:
正如您所注意到的和其他人提到的,如果您声明实现了
CloneNotSupportedException
,则几乎没有机会抛出>可克隆界面。此外,如果您不在重写的方法中执行任何新操作,则无需重写该方法。仅当需要对对象执行额外操作或需要将其公开时才需要覆盖它。
最终,最好还是避免它并使用其他方式来实现。
As much as the most of the answers here are valid, I need to tell that your solution is also how the actual Java API developers do it. (Either Josh Bloch or Neal Gafter)
Here is an extract from openJDK, ArrayList class:
As you have noticed and others mentioned,
CloneNotSupportedException
has almost no chance to be thrown if you declared that you implement theCloneable
interface.Also, there is no need for you to override the method if you don't do anything new in the overridden method. You only need to override it when you need to do extra operations on the object or you need to make it public.
Ultimately, it is still best to avoid it and do it using some other way.
仅仅因为 Java 的 Cloneable 实现被破坏,并不意味着您不能创建自己的 Cloneable 实现。
如果OP的真正目的是创建一个深度克隆,我认为可以创建一个这样的接口:
然后使用前面提到的原型构造函数来实现它:
以及另一个带有AClass对象字段的类:
这样你就可以轻松地深度克隆 BClass 类的对象,无需 @SuppressWarnings 或其他花哨的代码。
Just because java's implementation of Cloneable is broken it doesn't mean you can't create one of your own.
If OP real purpose was to create a deep clone, i think that it is possible to create an interface like this:
then use the prototype constructor mentioned before to implement it:
and another class with an AClass object field:
In this way you can easely deep clone an object of class BClass without need for @SuppressWarnings or other gimmicky code.