Что это за полевая копия, сделанная Object.clone ()? - PullRequest
12 голосов
/ 23 мая 2010

В Effective Java автор утверждает, что:

Если класс реализует Cloneable, Метод клона объекта возвращает полевая копия объекта; в противном случае это бросает CloneNotSupportedException.

Что я хотел бы знать, так это то, что он имеет в виду под полевым копированием. Означает ли это, что если класс имеет в памяти X байтов, он просто скопирует этот фрагмент памяти? Если да, то могу ли я предположить, что все типы значений исходного класса будут скопированы в новый объект?

class Point implements Cloneable{
    private int x;
    private int y;

    @Override
    public Point clone() {
        return (Point)super.clone();
    }
}

Если то, что делает Object.clone(), является копией поля за полем класса Point, я бы сказал, что мне не нужно явно копировать поля x и y, так как код, показанный выше будет более чем достаточно, чтобы сделать клон класса Point. То есть следующий бит кода является избыточным:

@Override
public Point clone() {
    Point newObj = (Point)super.clone();
    newObj.x = this.x; //redundant
    newObj.y = this.y; //redundant
}

Я прав?

Я знаю, что ссылки на клонированный объект будут автоматически указывать туда, куда указывали ссылки на исходный объект, я просто не уверен, что конкретно происходит с типами значений. Если бы кто-то мог ясно заявить, что спецификация алгоритма Object.clone() (на простом языке) была бы великолепна.

Ответы [ 4 ]

5 голосов
/ 23 мая 2010

Да, копирование поля за полем означает, что при создании нового (клонированного) объекта JVM будет копировать значение каждого поля из исходного объекта в клонированный объект.К сожалению, это означает, что у вас есть мелкая копия.Если вам нужна глубокая копия, вы можете переопределить метод клонирования.

class Line implements Cloneable {

    private Point start;
    private Point end;

    public Line() {
        //Careful: This will not happen for the cloned object
        SomeGlobalRegistry.register(this);
    }

    @Override
    public Line clone() {
        //calling super.clone is going to create a shallow copy.
        //If we want a deep copy, we must clone or instantiate
        //the fields ourselves
        Line line = (Line)super.clone();
        //assuming Point is cloneable. Otherwise we will
        //have to instantiate and populate it's fields manually
        line.start = this.start.clone();
        line.end = this.end.clone;
        return line;
    }
}

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

Лично я предпочитаю не использовать клонирование Java.Вместо этого я обычно создаю свои собственные методы «дублирования».

4 голосов
/ 23 мая 2010
newObj.x = this.x; //redundant
newObj.y = this.y; //redundant

верно - это избыточно, так как они уже будут скопированы методом Object clone ().

Думать об этом как о копии данных - это правильно. Примитивные типы копируются, и ссылки также копируются, поэтому они указывают на один и тот же объект. Например,

class A implements Cloneable {
  Object someObject;
}

A a = new A();
a.someObject = new Object();

A cloneA = (A)a.clone();
assert a.someObject==cloneA.someObject;
4 голосов
/ 23 мая 2010

Это означает мелкую копию - поля копируются, но если у вас есть какие-либо ссылки, то, на что они указывают, не копируются - у вас будет две ссылки на один и тот же объект, один в старом объекте и один вновый, клонированный объект.Однако для полей, имеющих примитивные типы, это сами данные, поэтому они копируются независимо.

1 голос
/ 23 мая 2010

Клон по умолчанию выполняет поверхностное копирование значений. Для примитивных значений этого достаточно, и никакой дополнительной работы не требуется.

Для объектов поверхностное копирование означает копирование только ссылки. Поэтому в этих случаях обычно требуется глубокая копия. Исключением является случай, когда ссылка указывает на неизменный объект. Неизменяемые объекты не могут изменять свое видимое состояние, поэтому их ссылки можно безопасно копировать. Например, это относится к перечислениям String, Integer, Float, (если они не были изменены по ошибке).

...