2009-01-24 15 views
17

Tengo un objeto que necesito copiar en Java. Necesito crear una copia y ejecutar algunas pruebas sin cambiar el objeto original.Copie un objeto en Java

Supuse que tenía que usar el método clone(), pero esto está protegido. Después de haber hecho algunas investigaciones en la red, puedo ver que esto puede ser anulado con un método público en mi clase. Pero no puedo encontrar una explicación sobre cómo hacer esto. ¿Como se puede hacer esto?

Además, ¿es esta la mejor manera de lograr lo que necesito?

+0

¿Esto es para el propósito de prueba solamente (unidad de prueba), o ¿necesita esta funcionalidad en el código de producción real? – crunchdog

Respuesta

13

Algunas opciones:

  • Puede implementar Cloneable para su objeto y poner clone() método público. Consulte la explicación completa aquí: http://www.cafeaulait.org/course/week4/46.html
    Sin embargo, esto produce una copia superficial y podría no ser algo que desee.
  • Puede serializar y deserializar su objeto. Tendrá que implementar la interfaz Serializable para el objeto y todos sus campos.
  • Puede usar XStream para realizar la serialización a través de XML; no tendrá que implementar nada aquí.
7

Para código de prueba La serialización es quizás la respuesta más segura, especialmente si el objeto ya es Serializable, intente Apache Commons SerializationUtils para una implementación.

27

Otra opción mediante el uso de constructor de copia (de Java Practices):

public final class Galaxy { 

    public Galaxy (double aMass, String aName) { 
     fMass = aMass; 
     fName = aName; 
    } 

    /** 
    * Copy constructor. 
    */ 
    public Galaxy(Galaxy aGalaxy) { 
     this(aGalaxy.getMass(), aGalaxy.getName()); 
     //no defensive copies are created here, since 
     //there are no mutable object fields (String is immutable) 
    } 

    /** 
    * Alternative style for a copy constructor, using a static newInstance 
    * method. 
    */ 
    public static Galaxy newInstance(Galaxy aGalaxy) { 
     return new Galaxy(aGalaxy.getMass(), aGalaxy.getName()); 
    } 

    public double getMass() { 
     return fMass; 
    } 

    /** 
    * This is the only method which changes the state of a Galaxy 
    * object. If this method were removed, then a copy constructor 
    * would not be provided either, since immutable objects do not 
    * need a copy constructor. 
    */ 
    public void setMass(double aMass){ 
     fMass = aMass; 
    } 

    public String getName() { 
     return fName; 
    } 

    // PRIVATE ///// 
    private double fMass; 
    private final String fName; 

    /** 
    * Test harness. 
    */ 
    public static void main (String... aArguments){ 
     Galaxy m101 = new Galaxy(15.0, "M101"); 

     Galaxy m101CopyOne = new Galaxy(m101); 
     m101CopyOne.setMass(25.0); 
     System.out.println("M101 mass: " + m101.getMass()); 
     System.out.println("M101Copy mass: " + m101CopyOne.getMass()); 

     Galaxy m101CopyTwo = Galaxy.newInstance(m101); 
     m101CopyTwo.setMass(35.0); 
     System.out.println("M101 mass: " + m101.getMass()); 
     System.out.println("M101CopyTwo mass: " + m101CopyTwo.getMass()); 
    } 
} 
+4

No utilizaría getter en el constructor de copias. Primero, no los necesita, porque siempre tiene acceso a variables de instancia. En segundo lugar, si haces de esto una práctica, puedes reducir la encapsulación creando demasiados captadores. Y finalmente puede ser propenso a errores. Imagine que ajusta el valor en getMass() de lo que copiaría el valor ajustado en la variable de instancia del nuevo objeto, que modificaría el valor una segunda vez si llama a getMass en el nuevo objeto. –

+0

@Mr_and_Mrs_D, tienes razón, no lo vi en la primera lectura. El punto de Norbert sigue en pie. –

1

Joshua Bloch tiene some interesting things to say about cloneable. Dependiendo del tamaño/construcción del objeto, agregaría un constructor de copia al objeto, o serializar/deserializar usando una de las soluciones mencionadas anteriormente.

20

Hay dos enfoques populares. Una es proporcionar un método clone como usted mencionó, como tal.

public class C implements Cloneable { 
    @Override public C clone() { 
     try { 
      final C result = (C) super.clone(); 
      // copy fields that need to be copied here! 
      return result; 
     } catch (final CloneNotSupportedException ex) { 
      throw new AssertionError(); 
     } 
} 

Preste atención a los "campos de copia ... aquí!" parte. La inicial result es solo una copia superficial, lo que significa que si hay una referencia a un objeto, tanto el original como result compartirán el mismo objeto. Por ejemplo, si C contiene private int[] data, probablemente querrá copiar eso.

... 
final C result = (C) super.clone(); 
result.data = data.clone(); 
return result; 
... 

Tenga en cuenta que no es necesario copiar los campos primitivos, ya que su contenido se ha copiado, o los objetos inmutables, ya que no pueden cambiar de todos modos.

El segundo enfoque es proporcionar un constructor de copia.

public class C { 
    public C(final C c) { 
     // initialize this with c 
    } 
} 

O una fábrica de copias.

public class C { 
    public static C newInstance(final C c) { 
     return new C(c); 
    } 

    private C(final C c) { 
     // initialize this with c 
    } 
} 

Ambos enfoques tienen sus respectivas propiedades. clone es bueno porque es un método, por lo que no es necesario saber el tipo exacto. Al final, siempre debes terminar con una copia "perfecta". El constructor de copias es agradable porque la persona que llama tiene la oportunidad de decidir, como se puede ver en las Colecciones de Java.

final List c = ... 
// Got c from somewhere else, could be anything. 
// Maybe too slow for what we're trying to do? 

final List myC = new ArrayList(c); 
// myC is an ArrayList, with known properties 

Recomiendo elegir cualquier enfoque, el que más le convenga.

Utilizaría los otros enfoques, como la copia reflectiva o la serialización/deserialización inmediata, solo en pruebas unitarias. Para mí, se sienten menos apropiados para el código de producción, principalmente por cuestiones de rendimiento.

2

Hay varias formas de copiar objetos en Java (poco profundo o profundo).
Este Answer lo ayudará.

2

Puede usar la clase org.apache.commons.lang3.SerializationUtils para la clonación de objetos, - La clase debe implementar la interfaz Serializable.

  • NombredeClase CopiarObjeto = SerializationUtils.clone (classobjectTocopy)

SerializationUtils.clone también se apoya en la instancia Google App Engine