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

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

20 голосов
/ 24 января 2009

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

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

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

Ответы [ 7 ]

29 голосов
/ 24 января 2009

Другой вариант с использованием конструктора копирования (из 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());
    }
} 
20 голосов
/ 24 января 2009

Есть два популярных подхода. Одним из них является предоставление 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

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

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

14 голосов
/ 24 января 2009

Некоторые опции:

  • Вы можете реализовать Cloneable для вашего объекта и поставить метод clone() как открытый. Смотрите полное объяснение здесь: http://www.cafeaulait.org/course/week4/46.html
    Тем не менее, это дает мелкую копию и может быть не то, что вы хотите.
  • Вы можете сериализовать и десериализовать ваш объект. Вам нужно будет реализовать интерфейс Serializable для объекта и всех его полей.
  • Вы можете использовать XStream для выполнения сериализации через XML - вам не нужно ничего реализовывать здесь.
9 голосов
/ 24 января 2009

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

3 голосов
/ 01 июня 2017

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

  • ClassName copyobject = SerializationUtils.clone (classobjectTocopy)

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

2 голосов
/ 23 марта 2012

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

2 голосов
/ 24 января 2009

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

...