Реализация клона на LinkedList - PullRequest
4 голосов
/ 03 июня 2010

Я пытаюсь реализовать метод 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 ]

5 голосов
/ 03 июня 2010

Как вы правильно заметили, соглашение всегда должно вызывать 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 your DoubleLinkedList, который, если он не проходит через super.clone (), возвращает экземпляр DoubleLinkedList, который, в свою очередь, не может быть приведен к IntDoubleLinkedList. A ClassCastException будет брошено!

Так как super.clone() решает эту проблему? Хорошо, если все будут придерживаться соглашения о вызове super.clone() в переопределенном методе клонирования, в конечном итоге будет вызван Object.clone(), и эта реализация создаст экземпляр правильного типа (IntDoubleLinkedList в этом случае)!

1 голос
/ 03 июня 2010

Как уже объяснили другие, если вы собираетесь переопределить clone, вы должны соблюдать его контракт.

Если вам нравится то, что у вас сейчас есть, просто сделайте DoubleLinkedList, а не Cloneable и превратите вашу реализацию в конструктор копирования или статический метод фабрики. Статический метод фабрики имеет дополнительное преимущество, заключающееся в предоставлении небольшого вывода типа для аргументов универсального типа.

P.S. LinkedList - это двусвязный список.

0 голосов
/ 03 июня 2010

Причина, по которой "конвенция" заключается в вызове super.clone(), заключается в том, чтобы гарантировать, что конечный тип клонированного объекта соответствует клонируемому объекту. Например, если вы создаете свой собственный новый DoubleLinkedList в методе clone(), это хорошо, пока это хорошо, но позже, если подкласс не сможет переопределить clone(), он в итоге вернет клон, который является DoubleLinkedList вместо свой собственный класс. (Он также не сможет клонировать свои дополнительные поля, если таковые имеются, возможно!, Поэтому существуют большие проблемы.)

В этом смысле предпочтителен обычный метод, и он действительно неуклюж.

Обе реализации, однако, имеют похожую проблему: вы не копируете структуры данных. Клон всего лишь мелкий полицейский. Это, вероятно, не то, что ожидает звонящий. Вам нужно будет пройти и заменить каждое значение в DoubleLinkedList клоном значения, а также для других не примитивных полей.

В этом смысле обычный метод даст неправильный результат! Тебе нужен третий путь. Ваш первый метод, вероятно, почти работает, за исключением того, что вам нужно добавить element.clone() например.

0 голосов
/ 03 июня 2010

Если вы делаете это путем создания нового списка и добавления всех элементов из источника, если вы затем делаете что-то вроде:

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 будет «новым значением» в обоих списках (наоборот, то же самое; если вы измените его в l1, изменения появятся в l2). Правильный способ - клонировать каждый элемент и добавлять клон, чтобы убедиться, что списки независимы. Обратите внимание, что это происходит только в том случае, если вы изменяете свойства элементов, а не добавляете, перемещаете, удаляете их из списка.

Редактировать: только что понял, что, поскольку это связанный список, следующие / предыдущие элементы являются свойствами элемента, поэтому даже добавление, удаление влияет на оба списка.

...