2010-12-01 26 views
6

Necesito clonar una subclase en Java, pero en el punto donde ocurre esto, no sabré el tipo de subclase, solo la superclase. ¿Cuál es el mejor patrón de diseño para hacer esto?Subclases de clonación en Java

Ejemplo:

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

Respuesta

6

Si usted tiene el control de las clases que están tratando de copiar, a continuación, un método virtual es el camino a seguir:

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

(Lo ideal es hacer que las clases ya sea abstracto o con eficacia final.)

+0

No, no funciona. Pruebe esto: Bar bar = new Bar(); Foo foo = bar; foo.copy(). Invocará Foo.copy(), no Bar.copy() – ccleve

+2

@ user237815 No lo creo. 'Bar.copy' reemplaza (marcada con' @ Override') 'Foo.copy'. –

+0

Tom es correcto. La invocación de 'copy' ocurre en el tiempo de ejecución y la JVM llamará al método apropiado de 'copia' en función del tipo del objeto. –

0

La forma de hacerlo es crear un método newInstance() en Foo y Bar. Ambas implementaciones pueden crear un objeto del tipo correcto y devolverlo, el código de copiado solo tiene que saber que debe usar oldFoo.newInstance() para obtener una copia del objeto.

+0

Para mí, el nombre newInstance() implica un nuevo objeto no relacionado, no una copia/clon. –

+0

Esperaba una reacción como esta después de agregar la respuesta ... El nombre del método depende del contexto/conceptos que desea expresar (como copiar, clonar, getInstance, etc.). De modo que cualquier ejemplo de nombre de método utilizado en la respuesta es probablemente no sea lo que va a usar el OP. Para mí, el nombre newInstance señala una nueva instancia de objeto que en este caso se puede inicializar con los valores del objeto que ejecuta el nuevo método de instancia. (A diferencia de un nuevo método de instancia estático). – rsp

0

Si necesita clonar un objeto a fondo, la mejor manera es usar la serialización de Java. Esto requiere que el objeto implemente Serializable pero creará un objeto clonado completamente nuevo sin referencias compartidas al original.

Puede hacer que su clase primaria implemente Serializable, por lo que cada subclase la admitirá automáticamente.

Por último, esto probablemente se puede actualizar para usar flujos de datos en lugar de ByteArrayOutputStream para usar menos memoria y ser más rápido, no se notará si tiene objetos pequeños.

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

no estoy seguro de si este se adapte a sus necesidades con exactitud, pero me gustaría sugerir echar un vistazo a la Factory pattern.

3

La forma más estándar en Java sería hacer cada clase que puede ser clonado en práctica Cloneable, y anular Object.clone() con una versión pública que hace la clonación apropiadamente para esa clase (por defecto Object.clone() hace una copia superficial del objeto) .

Tenga en cuenta que mucha gente piensa que Cloneable/Object.clone() es un mal diseño.

2

Si implementa clone/clonable en el modo correcto no tendrá ningún problema de superclase. Esto se debe a que Object.clone() es un método muy especial, en una descripción muy breve: Object.clone() siempre devolverá un objeto del mismo tipo al que se invoca.

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

Así que la aplicación correcta del clon y clonable sería:

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

A continuación, la aplicación de la copiadora es fácil:

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

Este patrón/solución utiliza un combinación de constructores de copia y funciones virtuales tipadas reemplazadas, y propaga la instancia a cada superclase.

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