2010-05-23 12 views
11

En Effective Java, el autor afirma que:¿Qué es esta copia de campo por campo hecha por Object.clone()?

Si una clase implementa Cloneable, método clone de objeto devuelve una copia campo por campo del objeto; de lo contrario arroja CloneNotSupportedException.

Lo que me gustaría saber es a qué se refiere con la copia de campo por campo. ¿Significa que si la clase tiene X bytes en la memoria, simplemente copiará esa pieza de memoria? En caso afirmativo, ¿puedo suponer que todos los tipos de valores de la clase original se copiarán en el nuevo objeto?

class Point implements Cloneable{ 
    private int x; 
    private int y; 

    @Override 
    public Point clone() { 
     return (Point)super.clone(); 
    } 
} 

Si lo hace es Object.clone() un campo por campo copia de la clase Point, diría que yo no tenga que copiar de manera explícita campos x y y, siendo que incluya la palabra clave será más que lo suficiente como para hacer una copia de la clase Point. Es decir, el siguiente bit de código es redundante:

@Override 
public Point clone() { 
    Point newObj = (Point)super.clone(); 
    newObj.x = this.x; //redundant 
    newObj.y = this.y; //redundant 
} 

¿Estoy en lo cierto?

Sé que las referencias del objeto clonado apuntarán automáticamente hacia donde apuntaban las referencias del objeto original, pero no estoy seguro de qué ocurre específicamente con los tipos de valores. Si alguien pudiera decir claramente qué es la especificación del algoritmo Object.clone() (en un lenguaje fácil) sería genial.

Respuesta

5

Sí, un campo por campo de copia significa que cuando se crea el nuevo objeto (clonado), la JVM copiará el valor de cada campo del objeto original en el objeto clonado. Desafortunadamente, esto significa que tienes una copia superficial. Si desea una copia profunda, puede anular el método de clonación.

class Line implements Cloneable { 

    private Point start; 
    private Point end; 

    public Line() { 
     //Careful: This will not happen for the cloned object 
     SomeGlobalRegistry.register(this); 
    } 

    @Override 
    public Line clone() { 
     //calling super.clone is going to create a shallow copy. 
     //If we want a deep copy, we must clone or instantiate 
     //the fields ourselves 
     Line line = (Line)super.clone(); 
     //assuming Point is cloneable. Otherwise we will 
     //have to instantiate and populate it's fields manually 
     line.start = this.start.clone(); 
     line.end = this.end.clone; 
     return line; 
    } 
} 

También una cosa más importante acerca de la clonación es, el constructor del objeto clonado es nunca invocó (sólo los campos se copian). Entonces, si el constructor inicializa un objeto externo, o registra este objeto con algún registro, entonces eso no sucederá con el objeto clonado.

Personalmente prefiero no utilizar la clonación de Java. En cambio, generalmente creo mis propios métodos de "duplicación".

+0

¿Por qué no usarías la clonación de Java y escribirías tu propio método? ¿Alguna razón específica? – zengr

+1

Lee Java efectivo. Hay muchos de ellos. –

+0

http://www.javapractices.com/topic/TopicAction.do?Id=71 –

4

Significa una copia superficial: los campos se copian, pero si tiene alguna referencia, lo que estos señalan no se copia, tendrá dos referencias al mismo objeto, una en el objeto anterior y otra en el nuevo objeto clonado Sin embargo, para los campos que tienen tipos primitivos, el campo son los datos en sí mismos, por lo que se copian independientemente.

+0

Correcto, aunque si el método 'clone' no está implementado como' super.clone() 'it * might * no puede implementarse en el contrato. –

+0

Ah sí, esto es ciertamente cierto. Noté que en el ejemplo que extiende Cloneable, que parece incorrecto, lo implementaría y luego anularía el método de clonación (tal vez extender Cloneable es cómo funcionó en Java 1?). Supongo que la implementación de esto dice que "hey, tengo esta naturaleza especial clonable, clono correctamente y hago una copia manual profunda en mi método de clonación". Sin embargo, a menos que anule, clone() del objeto, como usted dice, realiza una copia superficial. –

3
newObj.x = this.x; //redundant 
newObj.y = this.y; //redundant 

que correcto - estos son redundantes, puesto que ellos ya han sido copiados por el clon de objetos() método.

Pensando en ello como una copia de datos es correcta. Los tipos primitivos se copian y las referencias también se copian para que apunten al mismo objeto. Por ejemplo,

class A implements Cloneable { 
    Object someObject; 
} 

A a = new A(); 
a.someObject = new Object(); 

A cloneA = (A)a.clone(); 
assert a.someObject==cloneA.someObject; 
1

El clon predeterminado realiza una copia superficial de los valores. Para valores primitivos, esto es suficiente y no se necesita trabajo adicional.

Para objetos, la copia superficial significa copiar solo la referencia. Por lo tanto, en estos casos, generalmente se necesita una copia profunda. La excepción para esto es cuando la referencia apunta a un objeto inmutable. Los objetos inmutables no pueden cambiar su estado aparente, por lo tanto, sus referencias se pueden copiar de forma segura. Por ejemplo, esto se aplica a String, Integer, Float, enumerations (si no se hace mutable por error).