2010-12-01 8 views
6

J'ai besoin de cloner une sous-classe en Java, mais au point où cela se produit, je ne connaîtrai pas le type de la sous-classe, seulement la super classe. Quel est le meilleur modèle de conception pour ce faire?Sous-classes de clonage en Java

Exemple:

class Foo { 
    String myFoo; 
    public Foo(){} 
    public Foo(Foo old) { 
     this.myFoo = old.myFoo; 
    } 
} 

class Bar extends Foo { 
    String myBar; 
    public Bar(){} 
    public Bar(Bar old) { 
     super(old); // copies myFoo 
     this.myBar = old.myBar; 
    } 
} 

class Copier { 
    Foo foo; 

    public Foo makeCopy(Foo oldFoo) { 

     // this doesn't work if oldFoo is actually an 
     // instance of Bar, because myBar is not copied 
     Foo newFoo = new Foo(oldFoo); 
     return newFoo; 

     // unfortunately, I can't predict what oldFoo's the actual class 
     // is, so I can't go: 
     // if (oldFoo instanceof Bar) { // copy Bar here } 
    } 
} 

Répondre

6

Si vous avez le contrôle des classes que vous essayez de copier, puis une méthode virtuelle est la voie à suivre:

class Foo { 
    ... 
    public Foo copy() { 
     return new Foo(this); 
    } 
} 
class Bar extends Foo { 
    ... 
    @Override public Bar copy() { 
     return new Bar(this); 
    } 
} 

(idéalement faire des classes soit abstraite ou efficace final.)

+0

Non, cela ne fonctionne pas. Essayez ceci: Barre de barre = nouvelle barre(); Foo foo = bar; foo.copy(). Il va appeler Foo.copy(), pas Bar.copy() – ccleve

+2

@ user237815 Je ne pense pas. 'Bar.copy' remplace (vérifiée avec' @ Override') 'Foo.copy'. –

+0

Tom a raison. L'appel de 'copy' se produit au moment de l'exécution et la machine virtuelle Java appellera la méthode' copy' appropriée en fonction du type de l'objet. –

0

La méthode consiste à créer une méthode newInstance() dans Foo et Bar. Les deux implémentations peuvent créer un objet du type correct et le renvoyer, le code de copie doit seulement savoir qu'il doit utiliser oldFoo.newInstance() pour obtenir une copie de l'objet.

+0

Pour moi, le nom newInstance() implique un nouvel objet indépendant, pas une copie/clone. –

+0

Je m'attendais à une réaction comme celle-ci après avoir ajouté la réponse ... Le nom de la méthode dépend du contexte/des concepts que vous voulez exprimer (comme copy, clone, getInstance, etc.) probablement pas ce que le PO va utiliser. Pour moi, le nom newInstance signale une nouvelle instance d'objet qui dans ce cas peut être initialisée avec les valeurs de l'objet exécutant la méthode newInstance. (Par opposition à une méthode static newInstance.) – rsp

0

Si vous devez cloner en profondeur un objet, le meilleur moyen est d'utiliser la sérialisation Java. Cela nécessite que l'objet implémente Serializable mais crée un objet cloné complètement nouveau sans références partagées à l'original.

Vous pourriez avoir votre classe primaire implémentable Serializable, ainsi chaque sous-classe le supportera automatiquement. Enfin, cela peut probablement être mis à jour pour utiliser des flux canalisés plutôt que ByteArrayOutputStream pour utiliser moins de mémoire et être plus rapide, mais cela ne sera pas perceptible si vous avez de petits objets.

public static<T> T clone(T object) { 
    try { 
     ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 
     ObjectOutputStream out = new ObjectOutputStream(bOut); 
     out.writeObject(object); 
     out.close(); 

     ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray())); 
     T copy = (T)in.readObject(); 
     in.close(); 

     return copy; 
    } 
    catch(Exception e) { 
     throw new RuntimeException(e); 
    } 
} 
0

Je ne sais pas si cela correspond exactement à vos besoins, mais je vous suggère de jeter un oeil à la Factory pattern.

3

La façon la plus standard Java serait de faire de chaque classe qui peut être cloné mettre en œuvre Cloneable, et passer outre Object.clone() avec une version publique qui fait le clonage appropiately pour cette classe (par défaut Object.clone() fait une copie superficielle de l'objet) .

Notez que beaucoup de gens pensent que Cloneable/Object.clone() est mauvaise conception.

2

Si vous implémentez clone/clonable dans le de façon correcte vous n'aurez aucun problème de super classe. C'est parce que Object.clone() est une méthode très spéciale - dans une description très brève: Object.clone() renverra toujours un objet du même type qu'il est appelé.

@see http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#clone%28%29

Ainsi, la mise en œuvre correcte du clone et clonable serait:

class Foo implements Clonable{ 
    String myFoo; 
    ... 
    Foo clone() { 
     try { 
     Foo clone = (Foo) super.clone(); 
     clone.myFoo = this.myFoo; 
     return clone; 
     } catch (CloneNotSupportedException e) { 
     throw new RuntimeException("this could never happen", e); 
     } 
    } 
} 

class Bar extends Foo { 
    String myBar; 
    ... 
    Bar clone() { 
     try { 
     Bar clone = (Bar) super.clone(); 
     clone.myBar = this.myBar(); 
     return clone; 
     } catch (CloneNotSupportedException e) { 
     throw new RuntimeException("this could never happen", e); 
     } 
    } 
} 

Ensuite, la mise en œuvre du copieur est facile:

class Copier { 
    Foo foo; 

    /** 
    * @return return a Foo if oldFoo is of class Foo, return Bar if oldClass is of class Bar. 
    */ 
    public Foo makeCopy(Foo oldFoo) { 
    return oldFoo.clone(); 
    } 
} 
0

Ce modèle/solution utilise un combinaison de constructeurs de copie et de fonctions virtuelles typées surchargées, et propage l'instance à chaque superclasse.

public class A { 
    private String myA; 

    public A(A a) { 
     myA = a.myA; 
    } 

    public A copy() { 
     return new A(this); 
    } 
} 

public class B extends A { 
    private String myB; 

    public B(B b) { 
     super(b); 
     myB = b.myB; 
    } 

    public B copy() { 
     return new B(this); 
    } 
} 

public class C extends B { 
    private String myC; 

    public C(C c) { 
     super(c); 
     this.myC = c.myC; 
    } 

    public C copy() { 
     return new C(this); 
    } 
}