Java 如何正确覆盖克隆方法?
我需要在一个没有超类的对象中实现深度克隆 处理超类(即Java 如何正确覆盖克隆方法?,java,clone,cloning,cloneable,Java,Clone,Cloning,Cloneable,我需要在一个没有超类的对象中实现深度克隆 处理超类(即对象)抛出的选中的CloneNotSupportedException的最佳方法是什么 一位同事建议我按以下方式处理: @Override public MyObject clone() { MyObject foo; try { foo = (MyObject) super.clone(); } catch (CloneNotSupportedException e) {
对象
)抛出的选中的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社区,看看是否还有其他见解可以包含在内。谢谢 你一定要使用克隆吗?大多数人都同意Java的
clone
已经崩溃了
如果你读过我书中有关克隆的内容,尤其是读过字里行间的内容,你就会知道我认为clone
已经彻底崩溃了。[…]令人遗憾的是,Cloneable
被破坏了,但它确实发生了
您可以在他的《有效Java第二版》第11项:明智地覆盖clone
中阅读更多关于该主题的讨论。他建议改为使用复制构造函数或复制工厂
他接着写了一页又一页,如果你觉得必须的话,你应该如何实现clone
。但他最后说:
所有这些复杂性真的有必要吗?很少地如果您扩展了一个实现了Cloneable
的类,那么除了实现一个性能良好的clone
方法之外,您别无选择。否则,最好提供对象复制的替代方法,或者干脆不提供该功能
重点是他的,不是我的
既然您明确表示除了实现
clone
,您别无选择,那么在这种情况下您可以做些什么:确保MyObject扩展java.lang.Object实现java.lang.Cloneable
。如果是这种情况,那么您可以保证永远不会捕获CloneNotSupportedException
。按照一些人的建议抛出AssertionError
,这似乎是合理的,但您也可以添加一条注释,解释为什么在这种特殊情况下,catch块永远不会被输入
或者,正如其他人所建议的,您可以实现
clone
,而无需调用super.clone
有时实现副本构造函数更简单:
public MyObject (MyObject toClone) {
}
它省去了处理
CloneNotSupportedException
的麻烦,可以处理final
字段,您不必担心返回的类型。用于制作深度副本。这不是最快的解决方案,但它不取决于类型。有两种情况下会抛出CloneNotSupportedException
:
Cloneable
(假设实际克隆最终遵循对象的克隆方法)。如果您在中编写此方法的类实现了Cloneable
,那么这种情况将永远不会发生(因为任何子类都将适当地继承它)
try
块中直接调用超类的方法,即使从调用super.clone()
的子类调用也是如此),而前一种情况不应该发生,因为您的类显然应该实现Cloneable
基本上,您应该确定地记录错误,但是在这个特定的实例中,只有当您弄乱了类的定义时,才会发生错误。因此,将其视为选中版本的NullPointerException
(或类似版本)-如果代码正常运行,则永远不会抛出它
在其他情况下,您需要为这种情况做好准备-无法保证给定对象是可克隆的,因此在捕获异常时,您应该根据这种情况采取适当的操作(继续使用现有对象,采取另一种克隆策略,例如序列化反序列化,如果您的方法需要cloneable提供的参数,则抛出
非法ParameterException
,等等)
编辑:虽然总的来说我应该指出是的,
clone()确实很难正确地执行,而且调用方很难知道返回值是否是他们想要的,当你考虑深VS浅克隆时,这样做是加倍的。通常最好避免完全地使用另一种机制。 你的代码工作方式非常接近“规范”。不过,我会在catch中抛出一个
AssertionError
。它表示永远不应该到达那一行
catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
您可以实现受保护的复制构造函数,如下所示:
/* This is a protected copy constructor for exclusive use by .clone() */
protected MyObject(MyObject that) {
this.myFirstMember = that.getMyFirstMember(); //To clone primitive data
this.mySecondMember = that.getMySecondMember().clone(); //To clone complex objects
// etc
}
public MyObject clone() {
return new MyObject(this);
}
公共类MyObject实现可克隆、可序列化{
@凌驾
@抑制警告(value=“未选中”)
受保护的MyObject克隆(){
ObjectOutputStream oos=null;
ObjectInputStream ois=null;
试一试{
ByteArrayOutputStream bOs=新建ByteArrayOutputStream();
oos=新对象输出流(bOs);
oos.writeObject(本);
ois=newObjectInputStream(newbytearrayinputstream(bOs.toByteArray());
返回(MyObject)ois.readObject();
}捕获(例外e){
//一些严重错误:/
返回null;
}最后{
如果(oos!=null)
试一试{
oos.close();
}捕获(IOE异常){
}
如果(ois!=null)
试一试{
ois.close();
}捕获(IOE异常){
}
}
}
}
虽然这里的大多数答案都是有效的,但我需要告诉大家,您的解决方案也是实际的Java API开发人员如何做到的
public class MyObject implements Cloneable, Serializable{
@Override
@SuppressWarnings(value = "unchecked")
protected MyObject clone(){
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
ByteArrayOutputStream bOs = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bOs);
oos.writeObject(this);
ois = new ObjectInputStream(new ByteArrayInputStream(bOs.toByteArray()));
return (MyObject)ois.readObject();
} catch (Exception e) {
//Some seriouse error :< //
return null;
}finally {
if (oos != null)
try {
oos.close();
} catch (IOException e) {
}
if (ois != null)
try {
ois.close();
} catch (IOException e) {
}
}
}
}
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
public interface Cloneable<T> {
public T getClone();
}
public class AClass implements Cloneable<AClass> {
private int value;
public AClass(int value) {
this.vaue = value;
}
protected AClass(AClass p) {
this(p.getValue());
}
public int getValue() {
return value;
}
public AClass getClone() {
return new AClass(this);
}
}
public class BClass implements Cloneable<BClass> {
private int value;
private AClass a;
public BClass(int value, AClass a) {
this.value = value;
this.a = a;
}
protected BClass(BClass p) {
this(p.getValue(), p.getA().getClone());
}
public int getValue() {
return value;
}
public AClass getA() {
return a;
}
public BClass getClone() {
return new BClass(this);
}
}