4

我正在尝试clone()DoubleLinkedList. 现在,问题是通过“约定”实现它比仅仅创建一个新的DoubleLinkedList并用我当前的 DoubleLinkedList 的所有元素填充它要麻烦得多。

这样做时我没有看到任何不便之处吗?

这是我目前的方法:

@Override
public DoubleLinkedList<T> clone() {
    DoubleLinkedList<T> dll = new DoubleLinkedList<T>();

    for (T element : dll) {
        dll.add(element);
    }

    return 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());
    }
}
4

4 回答 4

5

正如您正确指出的那样,惯例是始终super.clone()clone(). 来自API 文档Object#clone()

按照约定,返回的对象应该通过调用 super.clone 来获取。如果一个类及其所有超类(Object 除外)都遵守这个约定,那么 x.clone().getClass() == x.getClass() 就是这种情况。

您的第一次尝试(不使用super.clone())有以下问题:

假设我有

class IntDoubleLinkedList extends DoubleLinkedList<Integer> implements Cloneable

(这IntDoubleLinkedList不会打扰覆盖clone()),我运行以下代码:

IntDoubleLinkedList idll = new IntDoubleLinkedList();
IntDoubleLinkedList idll2 = (IntDoubleLinkedList) idll.clone();

会发生什么? 的 clone 方法DoubleLinkedList将被执行,如果它不通过 super.clone(),它会返回一个实例,DoubleLinkedList而该实例又不能被强制转换为IntDoubleLinkedList. AClassCastException会被扔!

那么如何super.clone()解决这个问题呢?好吧,如果每个人都坚持调用super.clone()重写的克隆方法的约定,Object.clone()最终会被调用,并且这个实现将创建一个适当类型的实例(IntDoubleLinkedList在这种情况下)!

于 2010-06-03T11:47:14.000 回答
1

正如其他人所解释的,如果你要覆盖clone你应该遵守它的合同。

如果您喜欢当前拥有它的方式,请DoubleLinkedList不要Cloneable将您的实现转换为复制构造函数或静态工厂方法。静态工厂方法还具有为泛型类型参数提供一点类型推断的额外好处。

PS一个双向链表。LinkedList

于 2010-06-03T11:54:50.557 回答
0

调用“约定”的原因super.clone()是确保克隆对象的最终类型与正在克隆的对象匹配。例如,如果您DoubleLinkedListclone()方法中实例化您自己的 new,那么现在这很好,但稍后如果子类无法覆盖clone(),它将最终返回一个克隆,DoubleLinkedList而不是它自己的类。(它也可能无法克隆其附加字段,如果有的话!所以有更大的问题。)

从这个意义上说,传统方法是首选,它确实很笨重。

然而,这两种实现都有一个类似的问题:您没有深度复制数据结构。克隆人只是一个肤浅的警察。这可能不是调用者所期望的。您需要遍历并用值DoubleLinkedList的克隆替换每个值,对于其他非原始字段也是如此。

从这个意义上说,传统方法在这里会给出错误的结果!你需要第三种方式。您的第一种方法可能几乎可以工作,除了您需要添加element.clone()例如。

于 2010-06-03T11:49:55.103 回答
0

如果您通过创建一个新列表并添加源中的所有元素来做到这一点,那么您可以执行以下操作:

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");

foo.property will be "new value" in both lists (the same the other way around; if you change it in l1, changes will appear in l2). The correct way would be to actually clone every element and the add the clone to ensure the lists are independent. Take note that this only happens if you change properties of the elements, not if you add, move, delete them from the list.

Edit: just realized that since it's a linked list, the next/previous elements are properties of the element, so even adding, deleting, will affect both list.

于 2010-06-03T11:34:32.800 回答