web-dev-qa-db-fra.com

Comment cloner un objet Java avec la méthode clone ()

Je ne comprends pas le mécanisme de clonage d'un objet personnalisé. Par exemple:

public class Main{

    public static void main(String [] args) {

        Person person = new Person();
        person.setFname("Bill");
        person.setLname("Hook");

        Person cloned = (Person)person.clone();
        System.out.println(cloned.getFname() + " " + cloned.getLname());
    }
}

class Person implements Cloneable{

    private String fname;
    private String lname;

    public Object clone() {

        Person person = new Person();
        person.setFname(this.fname);
        person.setLname(this.lname);
        return person;
    }

    public void setFname(String fname) {
        this.fname = fname;
    }

    public void setLname(String lname){
        this.lname = lname;
    }

    public String getFname(){
        return fname;
    }

    public String getLname() {
        return lname;
    }
}

C'est un exemple qui montre la bonne façon de cloner comme dans les livres. Mais je peux supprimer les implémentations Cloneable dans la définition de nom de classe et je reçois le même résultat.

Je ne comprends donc pas la proposition de Cloneable et pourquoi la méthode clone () est définie dans la classe Object?

18
Dima Zelinskyi

La méthode de clonage est destinée à faire une copie complète. Assurez-vous de bien comprendre la différence entre les copies profondes et peu profondes. Dans votre cas, un constructeur de copie peut être le modèle que vous souhaitez. Dans certains cas, vous ne pouvez cependant pas utiliser ce modèle, par exemple parce que vous sous-classez la classe X et que vous n'avez pas accès au constructeur de X dont vous avez besoin. Si X remplace correctement sa méthode de clonage (si nécessaire), vous pouvez effectuer une copie de la manière suivante:

class Y extends X implements Cloneable {

    private SomeType field;    // a field that needs copying in order to get a deep copy of a Y object

    ...

    @Override
    public Y clone() {
        final Y clone;
        try {
            clone = (Y) super.clone();
        }
        catch (CloneNotSupportedException ex) {
            throw new RuntimeException("superclass messed up", ex);
        }
        clone.field = this.field.clone();
        return clone;
    }

}

En général, lors de la substitution de votre méthode de clonage:

  • Rendre le type de retour plus spécifique
  • Commencez par appeler super.clone()
  • N'incluez pas la clause throws lorsque vous savez que clone() fonctionnera également pour toute sous-classe (faiblesse du modèle de clone; rendre la classe finale si possible)
  • Laissez les champs immuables et primitifs seuls, mais clonez manuellement les champs d'objets mutables après l'appel à super.clone() (une autre faiblesse du modèle de clone, car ces champs ne peuvent pas être rendus définitifs)

La méthode clone() de Object (qui sera éventuellement appelée lorsque toutes les superclasses obéissent au contrat) effectue une copie superficielle et prend soin du type d'exécution correct du nouvel objet. Notez qu'aucun constructeur n'est appelé dans l'ensemble du processus.

Si vous souhaitez pouvoir appeler clone() sur des instances, implémentez l'interface Cloneable et rendez la méthode publique. Si vous ne voulez pas pouvoir l'appeler sur des instances, mais vous voulez vous assurer que les sous-classes peuvent appeler leur super.clone() et obtenir ce dont elles ont besoin, alors n'implémentez pas Cloneable et conservez la méthode protected si votre superclasse ne l'a pas déjà déclarée publique.

Le modèle de clonage est difficile et comporte de nombreux pièges. Assurez-vous que c'est ce dont vous avez besoin. Considérez les constructeurs de copie ou une méthode d'usine statique.

15
Rinke

La clone() de la classe Object fait une copie superficielle de la mémoire au lieu d'appeler des méthodes comme le constructeur. Afin d'appeler clone() sur tout objet qui n'implémente pas clone() lui-même, vous devez implémenter l'interface Clonable.

Si vous remplacez la méthode clone(), vous n'avez pas à implémenter cette interface.

Tout comme le dit JavaDoc, cela entraînerait une exception:

class A {
  private StringBuilder sb; //just some arbitrary member
}

...

new A().clone(); //this will result in an exception, since A does neither implement Clonable nor override clone()

Si A dans l'exemple implémentait Clonable l'appel de clone() (la version Object) entraînerait une nouvelle instance A qui fait référence très identique StringBuilder, c'est-à-dire que les modifications apportées à sb dans l'instance clonée entraîneraient des modifications dans sb dans l'instance A d'origine.

Cela signifie avec une copie superficielle et c'est une des raisons pour lesquelles il est généralement préférable de remplacer clone().

Edit: tout comme un sidenote, l'utilisation de la covariance de type de retour rendrait votre clone() plus explicite:

public Person clone() {
  ...
}
8
Thomas

La JVM est capable de cloner l'objet pour vous, et vous n'êtes donc pas censé construire une nouvelle personne par vous-même. Utilisez simplement ce code:

class Person implements Cloneable {
    // ...
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

ou

class Person implements Cloneable {
    // ...
    @Override
    public Object clone() {
        try {
            return super.clone();
        }
        catch (CloneNotSupportedException e) {
            throw new Error("Something impossible just happened");
        }
    }
}

Cela fonctionnera même si la classe Person est sous-classée, alors que votre implémentation de clone créera toujours une instance de Person (et non une instance de Employee pour un Employee, par exemple).

8
JB Nizet

Il n'est pas nécessaire de créer l'objet explicitement ici dans la méthode clone(). Le simple fait d'appeler super.clone() créera la copie de cet objet. Il exécutera le clone peu profond.

1
Gaurav Agarwal

le clone n'est pas mature comme le dit Joshua bloch:

http://www.artima.com/intv/bloch13.html

1
rt.jar

Dans votre exemple, vous ne faites pas de clonage réel. Vous êtes remplacé par la méthode clone () de la classe d'objets et vous disposez de votre propre implémentation. mais dans votre méthode de clonage, vous créez un nouvel objet Personne. et le renvoyer. Dans ce cas, l'objet réel n'est pas cloné.

Votre méthode de clonage devrait donc être comme:

public Object clone() {
      return super.clone();
  }

Ici, le clone sera géré par la méthode de superclasse.

0
Satya

C'est le même objectif qu'une telle interface. Il permet principalement aux méthodes (etc.) d'accepter N'IMPORTE QUEL objet clonable et d'avoir accès à la méthode dont elles ont besoin sans se limiter à un objet spécifique. Certes, Clonable est probablement l'une des interfaces less utiles à cet égard, mais il y a certainement des endroits où vous pouvez le vouloir. Si vous voulez plus d'une idée, considérez l'interface Comparable qui vous permet par exemple d'avoir des listes triées (parce que la liste n'a pas besoin de savoir ce qu'est l'objet, seulement qu'elles peuvent être comparées).

0
Thor84no

si vous ne déclarez pas d'interface clonable, vous devriez obtenir une exception CloneNotSupportException lorsque vous appelez la méthode clone.Si vous déclarez puis appelez la méthode clone, cela fera une copie superficielle.

0
cacert