77 votes

clone () vs constructeur de copie vs méthode d'usine?

J'ai fait un rapide google sur la mise en œuvre de clone() en Java, et trouve: http://www.javapractices.com/topic/TopicAction.do?Id=71

Il a le commentaire suivant:

copie des constructeurs statiques et méthodes de fabrique de fournir une alternative à cloner, et sont beaucoup plus faciles à mettre en œuvre.

Tout ce que je veux faire est de faire une copie en profondeur. La mise en œuvre de clone() semble faire beaucoup de sens, mais cette hautement google classé article me fait un peu peur.

Voici les questions que j'ai remarqué:

Copier les constructeurs ne fonctionnent pas avec les médicaments Génériques.

Voici quelques pseudo-code qui ne compile pas.

public class MyClass<T>{
   ..
   public void copyData(T data){
       T copy=new T(data);//This isn't going to work.    
   }
   ..
}

Exemple 1: Utilisation d'un constructeur de copie dans une classe générique.

Usine méthodes n'ont pas de noms standard.

C'est bien beau d'avoir une interface de code réutilisable.

public class MyClass<T>{
    ..
    public void copyData(T data){
        T copy=data.clone();//Throws an exception if the input was not cloneable
    }
    ..
}

Exemple 2: Utilisation d'un clone() dans une classe générique.

J'ai remarqué que le clone n'est pas une méthode statique, mais ne serait-il pas être encore nécessaire pour faire de profondeur des copies de tous les champs protégés? Lors de la mise en œuvre de clone(), l'effort supplémentaire pour lancer des exceptions à la non-clonable sous-classes qui semble insignifiant pour moi.

Ai-je raté quelque chose? Toutes les suggestions seraient appréciées.

51voto

Yishai Points 42417

Fondamentalement, le clone est cassé. Rien ne marche avec les génériques facilement. Si vous avez quelque chose comme ceci (raccourci pour obtenir le point à travers):

public class SomeClass<T extends Copyable> {


    public T copy(T object) {
        return (T) object.copy();
    }
}

interface Copyable {
    Copyable copy();
}

Puis, avec un avertissement du compilateur, vous pouvez obtenir le travail fait. Parce que les génériques sont effacés au moment de l'exécution, quelque chose qui fait une copie va avoir un avertissement du compilateur générant de la fonte. Il n'est pas évitable dans ce cas.. Il n'est pas obligatoire dans certains cas (merci, kb304), mais pas dans tous. Considérons le cas où vous avez à l'appui d'une sous-classe ou un inconnu de la classe implémentant l'interface (comme vous avez été l'itération sur une collection de copyables qui ne génère pas nécessairement de la même classe).

25voto

kd304 Points 8369

Il y a aussi le motif Builder. Voir Effective Java pour plus de détails.

Je ne comprends pas votre évaluation. Dans un constructeur de copie, vous connaissez parfaitement le type. Pourquoi est-il nécessaire d'utiliser des génériques?

 public class C {
   public int value;
   public C() { }
   public C(C other) {
     value = other.value;
   }
}
 

Il y avait une question similaire récemment ici .

 public class G<T> {
   public T value;
   public G() { }
   public G(G<? extends T> other) {
     value = other.value;
   }
}
 

Un échantillon exécutable:

 public class GenTest {
    public interface Copyable<T> {
        T copy();
    }
    public static <T extends Copyable<T>> T copy(T object) {
        return object.copy();
    }
    public static class G<T> implements Copyable<G<T>> {
        public T value;
        public G() {
        }
        public G(G<? extends T> other) {
            value = other.value;
        }
        @Override
        public G<T> copy() {
            return new G<T>(this);
        }
    }
    public static void main(String[] args) {
        G<Integer> g = new G<Integer>();
        g.value = 1;
        G<Integer> f = g.copy();
        g.value = 2;
        G<Integer> h = copy(g);
        g.value = 3;
        System.out.printf("f: %s%n", f.value);
        System.out.printf("g: %s%n", g.value);
        System.out.printf("h: %s%n", h.value);
    }
}
 

8voto

Peter Lawrey Points 229686

Java n'a pas de copie des constructeurs dans le même sens que le C++ n'.

Vous pouvez avoir un constructeur qui prend un objet du même type comme un argument, mais quelques cours de soutien. (moins que le nombre de ceux qui soutiennent clone mesure)

Pour un générique clone, j'ai une méthode d'assistance qui crée une nouvelle instance d'une classe et copies les champs à partir de l'original (une copie) à l'aide de réflexions (en fait quelque chose, comme des reflets, mais plus rapide)

Pour une copie en profondeur, une approche simple est de sérialiser l'objet et de le sérialiser.

BTW: Mon suggère, c'est d'utiliser des objets immuables, alors vous n'aurez pas besoin de les cloner. ;)

6voto

Alepac Points 1206

Je pense que la réponse de Yishai pourrait être améliorée afin que nous ne puissions avoir aucun avertissement avec le code suivant:

 public class SomeClass<T extends Copyable<T>> {

    public T copy(T object) {
        return object.copy();
    }
}

interface Copyable<T> {
    T copy();
}
 

Ainsi, une classe devant implémenter une interface copiable doit ressembler à ceci:

 public class MyClass implements Copyable<MyClass> {

    @Override
    public MyClass copy() {
        // copy implementation
        ...
    }

}
 

0voto

Un modèle qui peut fonctionner pour vous est la copie au niveau bean. Fondamentalement, vous utilisez un constructeur sans argument et appelez différents setters pour fournir les données. Vous pouvez même utiliser les différentes bibliothèques de propriétés de beans pour définir les propriétés relativement facilement. Ce n'est pas la même chose que de faire un clone (), mais pour de nombreuses raisons pratiques, ça va.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X