Скопируйте объект в Java

У меня есть объект, который мне нужно скопировать в Java. Мне нужно создать копию и запустить на ней несколько тестов, не изменяя сам исходный объект.

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

Кроме того, это лучший способ добиться того, что мне нужно?


person Tray    schedule 24.01.2009    source источник
comment
Это только для целей тестирования (модульное тестирование) или вам нужна эта функциональность в реальном производственном коде?   -  person crunchdog    schedule 02.11.2010


Ответы (7)


Другой вариант с использованием конструктора копирования (из Java Practices):

public final class Galaxy {

    public Galaxy (double aMass, String aName) {
        fMass = aMass;
        fName = aName;
    }

    /**
    * Copy constructor.
    */
    public Galaxy(Galaxy aGalaxy) {
        this(aGalaxy.getMass(), aGalaxy.getName());
        //no defensive copies are created here, since 
        //there are no mutable object fields (String is immutable)
    }

    /**
    * Alternative style for a copy constructor, using a static newInstance
    * method.
    */
    public static Galaxy newInstance(Galaxy aGalaxy) {
        return new Galaxy(aGalaxy.getMass(), aGalaxy.getName());
    }

    public double getMass() {
        return fMass;
    }

    /**
    * This is the only method which changes the state of a Galaxy
    * object. If this method were removed, then a copy constructor
    * would not be provided either, since immutable objects do not
    * need a copy constructor.
    */
    public void setMass( double aMass ){
        fMass = aMass;
    }

    public String getName() {
        return fName;
    }

    // PRIVATE /////
    private double fMass;
    private final String fName;

    /**
    * Test harness.
    */
    public static void main (String... aArguments){
        Galaxy m101 = new Galaxy(15.0, "M101");

        Galaxy m101CopyOne = new Galaxy(m101);
        m101CopyOne.setMass(25.0);
        System.out.println("M101 mass: " + m101.getMass());
        System.out.println("M101Copy mass: " + m101CopyOne.getMass());

        Galaxy m101CopyTwo = Galaxy.newInstance(m101);
        m101CopyTwo.setMass(35.0);
        System.out.println("M101 mass: " + m101.getMass());
        System.out.println("M101CopyTwo mass: " + m101CopyTwo.getMass());
    }
} 
person ecleel    schedule 24.01.2009
comment
Я бы не стал использовать геттер в конструкторе копирования. Во-первых, они вам не нужны, потому что у вас всегда есть доступ к переменным экземпляра. Во-вторых, если вы сделаете это самостоятельно, вы можете снизить инкапсуляцию, создав слишком много геттеров. И, наконец, это может быть подвержено ошибкам. Представьте, что вы настраиваете значение в getMass(), а затем копируете измененное значение в переменную экземпляра нового объекта, который настраивает значение во второй раз, если вы вызываете getMass для нового объекта. - person Norbert Hartl; 28.07.2010
comment
@Mr_and_Mrs_D, вы правы - я не заметил этого при первом прочтении. Точка зрения Норберта остается в силе. - person Bob Cross; 29.01.2014

Есть два популярных подхода. Один из них - предоставить метод clone, как вы упомянули, вот так.

public class C implements Cloneable {
    @Override public C clone() {
        try {
            final C result = (C) super.clone();
            // copy fields that need to be copied here!
            return result;
        } catch (final CloneNotSupportedException ex) {
            throw new AssertionError();
        }
}

Обратите внимание на "копировать поля... сюда!" часть. Первоначальный result является лишь поверхностной копией, а это означает, что если есть ссылка на объект, и оригинал, и result будут использовать один и тот же объект. Например, если C содержит private int[] data, вы, вероятно, захотите его скопировать.

...
final C result = (C) super.clone();
result.data = data.clone();
return result;
...

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

Второй подход заключается в предоставлении конструктора копирования.

public class C {
    public C(final C c) {
        // initialize this with c
    }
}

Или копировальная фабрика.

public class C {
    public static C newInstance(final C c) {
        return new C(c);
    }

    private C(final C c) {
        // initialize this with c
    }
}

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

final List c = ... 
// Got c from somewhere else, could be anything.
// Maybe too slow for what we're trying to do?

final List myC = new ArrayList(c);
// myC is an ArrayList, with known properties

Я рекомендую выбрать любой подход, который вам больше подходит.

Я бы использовал другие подходы, такие как рефлексивное копирование или немедленная сериализация/десериализация, только в модульных тестах. Мне они кажутся менее подходящими для производственного кода, в основном из-за проблем с производительностью.

person Ronald Blaschke    schedule 24.01.2009

Некоторые варианты:

  • Вы можете реализовать Cloneable для своего объекта и сделать метод clone() общедоступным. Полное объяснение см. здесь: http://www.cafeaulait.org/course/week4/46.html
    Тем не менее, это создает поверхностную копию и может быть не тем, что вам нужно.
  • Вы можете сериализовать и десериализовать свой объект. Вам нужно будет реализовать интерфейс Serializable для объекта и всех его полей.
  • Вы можете использовать XStream для сериализации через XML — здесь вам не нужно ничего реализовывать.
person Yoni Roit    schedule 24.01.2009

Для тестового кода сериализация, возможно, является самым безопасным ответом, особенно если объект уже является сериализуемым, попробуйте Apache Commons SerializationUtils для реализации.

person Gareth Davis    schedule 24.01.2009

Вы можете использовать класс org.apache.commons.lang3.SerializationUtils для клонирования объектов, - класс должен реализовывать интерфейс Serializable.

  • ClassName copyobject = SerializationUtils.clone (classobjectTocopy)

SerializationUtils.clone также поддерживается в экземпляре Google App Engine.

person A.G    schedule 01.06.2017

У Джошуа Блоха есть кое-что интересное о возможности клонирования. В зависимости от размера/конструкции объекта я бы добавил к объекту конструктор копирования или сериализовал/десериализовал, используя одно из решений, упомянутых выше.

person Brian Agnew    schedule 24.01.2009

Есть несколько способов скопировать объект в java (мелкий или глубокий).
Этот Ответ поможет вам.

person Chandra Sekhar    schedule 23.03.2012