2010-12-01 6 views
6

Ich muss eine Unterklasse in Java klonen, aber an dem Punkt im Code, wo dies passiert, werde ich den Unterklasse-Typ nicht kennen, nur die Super-Klasse. Was ist das beste Designmuster dafür?Cloning-Unterklassen in Java

Beispiel:

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 } 
    } 
} 

Antwort

6

Wenn Sie die Kontrolle der Klassen haben Sie zu kopieren versuchen, dann eine virtuelle Methode ist der Weg nach vorn:

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

(Im Idealfall entweder Klassen abstrakt machen oder effektiv final.)

+0

Nein, funktioniert nicht. Probieren Sie es aus: Bar bar = new Bar(); Foo foo = Stange; foo.copy(). Es wird Foo.copy() aufrufen, nicht Bar.Kopie() – ccleve

+2

@ user237815 Ich glaube nicht. 'Bar.copy' überschreibt (markiert mit' @ Override') 'Foo.copy'. –

+0

Tom hat Recht. Der Aufruf von 'copy' erfolgt zur Laufzeit und die JVM ruft die entsprechende' copy'-Methode basierend auf dem Objekttyp auf. –

0

Der Weg dies zu tun, ist eine Methode newInstance() in Foo und Bar erstellen. Beide Implementierungen können ein Objekt des richtigen Typs erstellen und zurückgeben, dass der Kopiercode nur wissen muss, dass er oldFoo.newInstance() verwenden muss, um eine Kopie des Objekts zu erhalten.

+0

Für mich bedeutet der Name newInstance() ein neues nicht verwandtes Objekt, keine Kopie/kein Klon. –

+0

Ich habe eine Reaktion wie folgt erwartet, nachdem ich die Antwort hinzugefügt habe ... Der Methodenname hängt von dem Kontext/den Konzepten ab, die du ausdrücken willst (wie kopieren, klonen, getInstance, etc.) Also ist jedes Methodennamenbeispiel in der Antwort wahrscheinlich nicht, was das OP benutzen wird. Der Name newInstance signalisiert mir eine neue Objektinstanz, die in diesem Fall mit den Werten des Objekts, das die Methode newInstance ausführt, initialisiert werden kann. (Im Gegensatz zu einer statischen newInstance-Methode.) – rsp

0

Wenn Sie ein Objekt tiefklonen müssen, verwenden Sie am besten die Java-Serialisierung. Dies erfordert, dass das Objekt Serializable implementiert, aber es wird ein komplett neues, geklontes Objekt ohne gemeinsame Referenzen zum Original erzeugen.

Sie könnten Ihre primäre Klasse Serializable implementieren lassen, wodurch jede Unterklasse diese automatisch unterstützt.

Schließlich kann dies wahrscheinlich aktualisiert werden, Piped Streams anstelle von ByteArrayOutputStream zu verwenden, um weniger Speicher zu verwenden und schneller zu sein, es wird jedoch nicht auffallen, wenn Sie kleine Objekte haben.

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

Ich bin mir nicht sicher, ob dies genau Ihren Anforderungen entspricht, aber ich würde vorschlagen, einen Blick auf die Factory pattern nehmen.

3

Die meisten Standard-Weg in Java wäre, um jede Klasse zu machen, die Cloneable geklont werden kann, implementieren und Object.clone() mit einer öffentlichen Version überschreiben, die das Klonen für diese Klasse tut sachgemässer (standardmäßig Object.clone() macht eine flache Kopie des Objekts) .

Beachten Sie, dass viele Leute denken, dass Cloneable/Object.clone() schlechtes Design ist.

2

Wenn Sie Clone/Clonable im korrekten Weg implementieren, haben Sie kein Superklassenproblem. Dies liegt daran, dass Object.clone() eine sehr spezielle Methode ist - in einer sehr kurzen Beschreibung: Object.clone() gibt immer ein Objekt desselben Typs zurück, auf das es aufgerufen wurde.

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

So die korrekte Umsetzung des Klons und clonable wäre:

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

die Implementierung von Copier Dann ist einfach:

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

Dieses Muster/Lösung a verwendet Kombination von Kopierkonstruktoren und überschriebenen typisierten virtuellen Funktionen und leitet die Instanz an jede Oberklasse weiter.

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