Java 在LinkedList上实现克隆
我正在尝试在Java 在LinkedList上实现克隆,java,cloning,cloneable,Java,Cloning,Cloneable,我正在尝试在DoubleLinkedList上实现clone()方法。现在的问题是,通过“约定”实现它比仅仅创建一个新的DoubleLinkedList并用当前DoubleLinkedList的所有元素填充它要麻烦得多 这样做有什么不方便的地方吗 以下是我目前的做法: @Override public DoubleLinkedList<T> clone() { DoubleLinkedList<T> dll = new DoubleLinkedList<T&
DoubleLinkedList
上实现clone()
方法。现在的问题是,通过“约定”实现它比仅仅创建一个新的DoubleLinkedList
并用当前DoubleLinkedList的所有元素填充它要麻烦得多
这样做有什么不方便的地方吗
以下是我目前的做法:
@Override
public DoubleLinkedList<T> clone() {
DoubleLinkedList<T> dll = new DoubleLinkedList<T>();
for (T element : dll) {
dll.add(element);
}
return dll;
}
@覆盖
公共双链接列表克隆(){
DoubleLinkedList dll=新的DoubleLinkedList();
for(T元素:dll){
添加(元素);
}
返回dll;
}
以下是公约的内容:
@Override
public DoubleLinkedList<T> clone() {
try {
DoubleLinkedList<T> dll = (DoubleLinkedList<T>)super.clone();
//kinda complex code to copy elements
return dll;
} catch (CloneNotSupportedException e) {
throw new InternalError(e.toString());
}
}
@覆盖
公共双链接列表克隆(){
试一试{
DoubleLinkedList dll=(DoubleLinkedList)super.clone();
//复制元素的代码有点复杂
返回dll;
}捕获(CloneNotSupportedException e){
抛出新的内部错误(例如toString());
}
}
如果您通过创建一个新列表并添加源代码中的所有元素来执行此操作,如果您执行以下操作:
DoubleLinkedList<Foo> l1 = new DoubleLinkedList<Foo>();
l1.add (new Foo(params));
DoubleLinkedList<Foo> l2 = l1.clone();
Foo foo = l2.get(0);
foo.setProperty("new Value");
DoubleLinkedList l1=新的DoubleLinkedList();
l1.add(新的Foo(参数));
DoubleLinkedList l2=l1.clone();
Foo-Foo=l2.get(0);
foo.setProperty(“新价值”);
在两个列表中foo.property都是“新值”(反之亦然;如果在l1中更改它,更改将出现在l2中)。正确的方法是实际克隆每个元素,并添加克隆以确保列表是独立的。请注意,只有在更改元素的属性时才会发生这种情况,而不是在列表中添加、移动或删除元素时才会发生这种情况
编辑:刚刚意识到,由于它是一个链表,下一个/上一个元素是元素的属性,所以即使添加、删除,也会影响这两个列表。正如您正确指出的,惯例是在
clone()
实现之初总是调用super.clone()
。从:
按照惯例,返回的对象应该通过调用super.clone获得。如果一个类及其所有超类(对象除外)都遵守此约定,则x.clone().getClass()==x.getClass()
您的第一次尝试(未使用super.clone()
)出现以下问题:
如果我有
class IntDoubleLinkedList extends DoubleLinkedList<Integer> implements Cloneable
会发生什么?将执行DoubleLinkedList
的clone方法,如果该方法未通过super.clone(),则返回DoubleLinkedList
的实例,而该实例又无法转换为IntDoubleLinkedList
将抛出ClassCastException
强>
那么super.clone()
如何解决这个问题呢?好吧,如果每个人都坚持在重写的克隆方法中调用super.clone()
,那么最终将调用Object.clone()
,这个实现将创建一个适当类型的实例(IntDoubleLinkedList
) 之所以“约定”要调用super.clone()
,是为了确保克隆对象的最终类型与正在克隆的对象匹配。例如,如果您在clone()
方法中实例化自己的新DoubleLinkedList
,那么现在这很好,但是稍后如果子类无法重写clone()
它将返回一个克隆,它是DoubleLinkedList
,而不是它自己的类。(如果有其他字段,它也可能无法克隆!因此存在更大的问题。)
从这个意义上说,传统的方法是首选的,它确实是笨重的
然而,这两种实现都有一个类似的问题:没有深度复制数据结构。克隆人只是个肤浅的警察。这可能不是调用方所期望的。您需要遍历DoubleLinkedList
中的每个值,并将其替换为该值的克隆,对于其他非基本字段也是如此
从这个意义上讲,传统的方法将给出错误的结果!你需要第三条路。您的第一个方法可能只起作用,除了您需要添加
元素.clone()
例如。正如其他人所解释的,如果您要覆盖clone
,您应该遵守它的约定
如果您喜欢当前的方式,只需将DoubleLinkedList
设置为不可克隆的Cloneable
,并将实现转换为复制构造函数或静态工厂方法。静态工厂方法还有另外一个好处,即为泛型类型参数提供一些类型推断
p.S.是一个双链接列表。我希望使用浅层副本,因为API列表实现提供了浅层副本(例如LinkedList:“返回此LinkedList的浅层副本。(元素本身未克隆。)”),第一个方法(不调用super.clone())不符合约定。请参阅有关Object#clone()的文档和我的答案。请参阅我对aioobe关于约定的评论——如果类是最终类且不能子类化,则无需遵循该约定。您的答案听起来有点矛盾。让它像复制构造器一样工作并不强调人们应该通过克隆合同中建议的
super.clone
,返回的对象应该通过调用super.clone来获得。我告诉@Owned elysium忘记clone
,而是将其作为复制构造函数或静态工厂方法来编写,因为他已经想到了一个在其中一个方法中完全可以接受的实现。我会更新我的答案(我希望)更清楚。+1对于你的P.S.,我希望OP这样做是作为一种学习练习,而不是因为他们认为他们必须这样做,因为LinkedList只是单独链接的:让克隆实现浅拷贝没有什么错。事实上,这就是JavaAPI中的LinkedList
实现它的方式!我没有说这是错的;他问
IntDoubleLinkedList idll = new IntDoubleLinkedList();
IntDoubleLinkedList idll2 = (IntDoubleLinkedList) idll.clone();