Прототип Java клон не работает, как ожидалось? - PullRequest
0 голосов
/ 30 января 2019
public abstract class SwimmersPrototype implements Cloneable {
    public SwimmersPrototype clone() throws CloneNotSupportedException{
        return (SwimmersPrototype)super.clone();
    }
}

SwimmersPrototype.java

public class Swimmers extends SwimmersPrototype{
    List<Swimmer> swimmers;
    SortStrategy sortStrategy;

    public Swimmers() {
        swimmers = new ArrayList();
    }

    public List<Swimmer> sort() {
        return sortStrategy.sort(swimmers);
    }

    @Override
    public SwimmersPrototype clone() throws CloneNotSupportedException{
        SwimmersPrototype swp = (Swimmers)super.clone();
        return swp;
    }
}

Здесь я хочу клонировать объект этого класса, Пловцы.

public class Swim extends javax.swing.JFrame {
    Swimmers swimmers;
    Swimmers swimmersCopy;
    /**
     * Creates new form Swim
     */
    public Swim() {
        initComponents();
        swimmers = new Swimmers();
        fillSwimmers();
        fillTable(swimmers.getSwimmers());
        jTableListener();

        try {
            swimmersCopy = (Swimmers)swimmers.clone();

        } catch (CloneNotSupportedException ex) {
            Logger.getLogger(Swim.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

После вызова сортировки это изменяет список пловцовИсходный класс, объект копирования, swimmersCopy также изменен.

Здесь я просто отображаю список пловцов исходного объекта в таблице, я могу отсортировать его по свойству, но всякий раз, когда нажимается кнопка сортировки по умолчанию, яхотите перечислить пловцов по умолчанию, которые они вставили раньше?Но применяя сортировку, тоже изменяет список пловцов клонированного объекта?

Ответы [ 3 ]

0 голосов
/ 30 января 2019

Метод clone() по умолчанию для класса Object выполняет только поверхностное клонирование.Как говорит Javadoc , вам нужно реализовать глубокое клонирование самостоятельно:

По соглашению, объект, возвращаемый этим методом, должен быть независимым от этого объекта (который клонируется).Для достижения этой независимости может потребоваться изменить одно или несколько полей объекта, возвращаемого super.clone, перед его возвратом.Как правило, это означает копирование любых изменяемых объектов, которые составляют внутреннюю «глубокую структуру» клонируемого объекта, и замену ссылок на эти объекты ссылками на копии.

Это означает, что ваш оригиналисходный код просто выполняет поверхностную копию:

public class Swimmers extends SwimmersPrototype{
    List<Swimmer> swimmers;
    SortStrategy sortStrategy;

    public Swimmers() {
        swimmers = new ArrayList();
    }

    public List<Swimmer> sort() {
        return sortStrategy.sort(swimmers);
    }

    @Override
    public SwimmersPrototype clone() throws CloneNotSupportedException{
        SwimmersPrototype swp = (Swimmers)super.clone();
        return swp;
    }
}

Приведенная выше реализация метода clone() оставляет поле swimmers исходного объекта и его клон, указывающие на один и тот же List.Поэтому, когда вы изменяете список через один из объектов, вы видите то же самое изменение через другой объект.Чтобы выполнить глубокое копирование, вам нужно сделать следующее:

    @Override
    public Swimmers clone() throws CloneNotSupportedException{
        Swimmers swp = (Swimmers)super.clone();
        swp.swimmers = new ArrayList<>(swimmers);
        return swp;
    }

Однако, как вы сказали в комментарии, вы предпочитаете не копировать swimmers, а вместо этого реализовать политику копирования при записи.Во-первых, вы должны знать, что очень сложно получить абсолютно правильное копирование при записи в многопоточном приложении, если есть вероятность, что ваш объект Swimmers будет использоваться одновременно несколькими потоками.Если это не проблема в вашем случае, вы можете просто внести следующее изменение в метод sort():

    public List<Swimmer> sort() {
        swimmers = new ArrayList<>(swimmers);  // Copy on write
        return sortStrategy.sort(swimmers);
    }

Здесь мы создаем копию списка swimmers, чтобы убедиться, что мы несортировать список, которым потенциально можно поделиться.Делая копию, мы знаем, что этот объект является единственным, который содержит ссылку на список, который мы собираемся изменить.

Приведенная выше модификация излишне добавила бы издержки копии списка, даже если объект имеетникогда не был клонирован.Чтобы избежать этих издержек, вы можете добавить поле счетчика ссылок, которое вы увеличиваете в методе clone() (и подумать о некотором способе его уменьшения, когда клонированный объект больше не используется).Тогда вам нужно только скопировать список, если количество ссылок больше 1.

Кстати, как правило, перед использованием интерфейса Cloneable все Java-разработчики должны прочитать, что Джош Блох говорит об этом.в своей книге Эффективная Java .

0 голосов
/ 30 января 2019

Причина этого заключается в том, что метод clone() не копирует весь объект: созданная копия будет иметь общий список.Использование метода clone() является опасным способом.Например, вы можете использовать конструктор копирования:

public Swimmers(Swimmers s) {
    this.swimmers = new ArrayList<Swimmer>(s.swimmers);
}

Обратите внимание, что в этом примере конструктор копирования Swimmers вызывает конструктор копирования ArrayList.

0 голосов
/ 30 января 2019

Версия по умолчанию метода clone() создает поверхностную копию объекта.Мелкая копия объекта будет иметь точную копию всех полей исходного объекта.Если исходный объект имеет какие-либо ссылки на другие объекты как поля, то только ссылки этих объектов копируются в объект-клон, копии этих объектов не создаются.Это означает, что любые изменения, внесенные в эти объекты с помощью объекта-клона, будут отражены в исходном объекте или наоборот.Мелкая копия не на 100% не пересекается с оригинальным объектом.Мелкая копия не на 100% независима от исходного объекта.

Дополнительная информация

...