web-dev-qa-db-fra.com

Copiez un objet dans Java

J'ai un objet que je dois copier en Java. J'ai besoin de créer une copie et d'exécuter des tests dessus sans changer l'objet original lui-même.

J'ai supposé que j'avais besoin d'utiliser la méthode clone (), mais celle-ci est protégée. Après avoir fait quelques recherches sur le net, je peux voir que cela peut être annulé avec une méthode publique dans ma classe. Mais je ne trouve pas d'explication sur la façon de procéder. Comment cela pourrait-il être fait?

Est-ce aussi la meilleure façon de réaliser ce dont j'ai besoin?

20
Tray

Une autre option en utilisant Copy Constructor (from 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());
    }
} 
29
ecleel

Il existe deux approches populaires. L'une consiste à fournir une méthode clone comme vous l'avez mentionné, ainsi.

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();
        }
}

Faites attention aux "champs de copie ... ici!" partie. Le result initial n'est qu'une copie superficielle, ce qui signifie que s'il y a une référence à un objet, l'original et le result partageront le même objet. Par exemple, si C contient private int[] data vous voudrez probablement le copier.

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

Notez que vous n'avez pas besoin de copier les champs primitifs, car leur contenu est déjà copié, ou les objets immuables, car ils ne peuvent pas changer de toute façon.

La deuxième approche consiste à fournir un constructeur de copie.

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

Ou une usine de copie.

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

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

Les deux approches ont leurs propriétés respectives. clone est sympa parce que c'est une méthode, donc vous n'avez pas besoin de connaître le type exact. En fin de compte, vous devriez toujours vous retrouver avec une copie "parfaite". Le constructeur de copie est Nice car l'appelant a une chance de décider, comme le montrent les collections 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

Je recommande de choisir l'une ou l'autre approche, celle qui vous convient le mieux.

J'utiliserais les autres approches, comme la copie réfléchissante ou la sérialisation/désérialisation immédiate, dans les tests unitaires uniquement. Pour moi, ils se sentent moins appropriés pour le code de production, principalement en raison de problèmes de performances.

20
Ronald Blaschke

Quelques options:

  • Vous pouvez implémenter Cloneable pour votre objet et mettre la méthode clone() en public. Voir l'explication complète ici: http://www.cafeaulait.org/course/week4/46.html
    Cependant, cela produit une copie superficielle et peut ne pas être quelque chose que vous souhaitez.
  • Vous pouvez sérialiser et désérialiser votre objet. Vous devrez implémenter l'interface Serializable pour l'objet et tous ses champs.
  • Vous pouvez utiliser XStream pour effectuer la sérialisation via XML - vous n'aurez rien à implémenter ici.
14
Yoni Roit

Pour le code de test, la sérialisation est peut-être la réponse la plus sûre, surtout si l'objet est déjà sérialisable, essayez Apache Commons SerializationUtils pour une implémentation.

9
Gareth Davis

Vous pouvez utiliser la classe org.Apache.commons.lang3.SerializationUtils pour le clonage d'objet, - La classe doit implémenter l'interface Serializable.

  • ClassName copyobject = SerializationUtils.clone (classobjectTocopy)

SerializationUtils.clone est également pris en charge sur instance de Google App Engine

3
A.G

Il existe plusieurs façons de copier un objet en Java (peu profond ou profond).
Cette Réponse vous aidera.

2
Chandra Sekhar

Joshua Bloch a quelques choses intéressantes à dire sur le clonable . Selon la taille/construction de l'objet, j'ajouterais un constructeur de copie à l'objet, ou sérialiser/désérialiser en utilisant l'une des solutions mentionnées ci-dessus.

2
Brian Agnew