example - java deep clone
Java efectivo: Análisis del método clone() (5)
El contrato para la clone
especifica que "Por convención, el objeto devuelto debe obtenerse llamando a super.clone
". Si su clase no es definitiva y devuelve algo obtenido con una invocación del constructor, llamar a super.clone()
desde una subclase no devolverá el resultado esperado (en primer lugar, el tipo del objeto devuelto no será el tipo de la subclase , como devolvería el método clone()
nativo).
Considere lo siguiente del artículo 11 de Effective Java (Reemplace el clon con criterio) donde Josh Bloch está explicando qué está mal con el contrato clone()
.
Hay un número de problemas con este contrato. La disposición de que "no se llaman constructores" es demasiado fuerte. Un método de clonación de buen comportamiento puede llamar a los constructores para crear objetos internos a la clonación en construcción. Si la clase es definitiva, el clon puede incluso devolver un objeto creado por un constructor.
¿Alguien puede explicar lo que Josh Bloch está diciendo en el primer párrafo? "Si la clase es final
, el clone
puede incluso devolver un objeto creado por un constructor". ¿Qué tiene que ver la final
con el clone()
aquí?
Es porque las implementaciones típicas de clone () se ven así:
public class MyClass implements Cloneable {
protected Object clone() {
MyClass cloned = (MyClass) super.clone();
// set additional clone properties here
}
}
De esta manera, puede heredar el comportamiento de clonación de su superclase. En general, se supone que el resultado de una operación clone () devolverá el tipo de instancia correcto en función del objeto al que se llamó. Es decir. this.getClass ()
Por lo tanto, si una clase es definitiva, no tiene que preocuparse por una subclase que llame a super.clone () y que no obtenga el tipo de objeto correcto.
public class A implements Cloneable {
public Object clone() {
return new A();
}
}
public class B extends A {
public Object clone() {
B b = (B)super.clone(); // <== will throw ClassCastException
}
}
Pero, si A es final, nadie puede extenderlo y, por lo tanto, es seguro usar un constructor.
Si una clase no es definitiva, el clone
tiene que devolver la clase más derivada para la que fue llamada. Eso no puede funcionar con un constructor, porque el clone
no sabe a quién llamar. Si una clase es final, no puede tener subclases, por lo que no hay peligro en llamar a su constructor cuando se clona.
Una clase no tiene que proporcionar su propia implementación de clone
para poder ser clonada. Puede delegar eso a su superclase clonable. Aquí viene el truco: el clone
siempre debe devolver una instancia de la misma clase que la instancia a la que se llama. Eso es imposible de lograr en el caso descrito si se llama a un constructor explícito. Por otro lado, si el clone
overridng de clase es final, esto estaría bien.
Ver respuesta Jorado. Esta es la explicación. En clones adicionales tiene problemas en los campos finales, consulte: http://en.wikipedia.org/wiki/Clone_%28Java_method%29#clone.28.29_and_final_fields
También debe leer la entrevista de Josh en clon en: http://www.artima.com/intv/bloch13.html