Метод 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 .