用generics克隆

曾几何时有一堂课:

public class Scope<C extends Cloneable & Comparable> implements Comparable<Scope>, Cloneable, Serializable { private C starts; private C ends; ... @SuppressWarnings("unchecked") @Override public Object clone() { Scope scope; try { scope = (Scope) super.clone(); scope.setStarts((C) starts.clone()); // The method clone() from the type Object is not visible scope.setEnds((C) ends.clone()); // The method clone() from the type Object is not visible } catch (CloneNotSupportedException e) { throw new RuntimeException("Clone not supported"); } return scope; } } 

在对象中我们有:

 protected native Object clone() throws CloneNotSupportedException; 

而Cloneable接口是:

 public interface Cloneable { } 

我应该如何克隆这个?

我认为目前的绿色答案很糟糕 ,为什么你会问?

  • 它增加了很多代码
  • 它要求您列出要复制的所有字段并执行此操作
  • 当使用clone()时,这对列表不起作用(这是HashMap的clone()所说的:返回此HashMap实例的浅表副本:键和值自身未被克隆。)所以你最终手动完成它(这使得我哭了)

哦顺便说一下序列化也很糟糕,你可能不得不在整个地方添加Serializable(这也让我哭)。

那么解决方案是什么:

Java深度克隆库克隆库是一个小型的开源(apache许可证)java库,它深入克隆对象。 对象不必实现Cloneable接口。 实际上,这个库可以克隆任何java对象。 如果您不希望修改缓存对象或者只是想要创建对象的深层副本,则可以在缓存实现中使用它。

 Cloner cloner=new Cloner(); XX clone = cloner.deepClone(someObjectOfTypeXX); 

请访问http://code.google.com/p/cloning/查看

这就是为什么没有人喜欢Cloneable原因之一 。 它应该是一个标记接口,但它基本上没用,因为你不能克隆一个没有reflection的任意Cloneable对象。

实现此目的的唯一方法是使用public clone()方法创建自己的接口(不必将其称为“ clone() ”)。 以下是另一个StackOverflow问题的示例 。

稍微加油,但你可以用这个来为自己节省很多未来的悲伤:

  catch (CloneNotSupportedException e) { throw new RuntimeException("Clone not supported", e); } 

因此,当您获得堆栈跟踪时,您知道哪个对象导致了问题。

为了回答核心问题,你自己的接口实现了mmyers写的公共 clone(),并要求C也扩展它。

希望我已经解决了Java中generics克隆的问题:

 public class Generic { private T data; public Generic() { // ... } @SuppressWarnings("unchecked") @Override public Object clone() { Generic cloned = new Generic(); try { cloned.data = (T) data.getClass().getMethod("clone").invoke(data); } catch (Exception e) { // ... } return cloned; } } 

作为一般性注释,尽可能避免使用Object.clone()。 如果您可以控制相关代码,请改为实现复制构造函数。 请参阅此处获取信息。

如您所见,如果一个类试图实现Cloneable并且您想要深度克隆,那么所有组成对象都需要是不可变的,原始的,或者也需要是Cloneable。

通常,更好更简单的方法是创建复制构造函数。

 public class Scope> implements Comparable>, Serializable { private C starts; private C ends; public Scope(final Scope original) { starts = new C(original.starts); ends = new C(original.ends); // initialize all my other fields from "original" } } 

当然,你需要一个能够处理多态的C上的复制构造函数。

如果您无法访问或无法将源修改为C ,那么无论采用何种方法,任何复制方法都将非常困难且可能无法实现。 例如,无法复制enum实例。