clone()`clone()` es un método del lenguaje de programación Java para duplicar objetos . En Java, los objetos se manipulan mediante variables de referencia, y no existe un operador para copiar un objeto; el operador de asignación duplica la referencia, no el objeto. El método `clone()` proporciona esta funcionalidad faltante.
Descripción general
Las clases que deseen funcionalidad de copia deben implementar algún método para hacerlo. Hasta cierto punto, esa función la proporciona " Object.clone()".
clone()Funciona como un constructor de copia. Normalmente, llama al clone()método de su superclase para obtener la copia, y así sucesivamente hasta que finalmente llega al método Objectde la clase base clone(). El clone()método especial de la clase baseObject proporciona un mecanismo estándar para duplicar objetos.
El método de la clase crea y devuelve una copia del objeto, con la misma clase y con todos los campos con los mismos valores. Sin embargo, lanza una excepción a menos que el objeto sea una instancia de una clase que implemente la interfaz marker .Objectclone()Object.clone()CloneNotSupportedExceptionCloneable
La implementación predeterminada Object.clone()realiza una copia superficial . Cuando una clase requiere una copia profunda u otro comportamiento personalizado, debe implementarlo en su propio clone()método después de obtener la copia de la superclase.
La sintaxis para realizar llamadas cloneen Java es (suponiendo objque es una variable de un tipo de clase que tiene un clone()método público):
Copia del objeto = obj.clone ( );o comúnmente
Copia de MyClass = ( MyClass ) obj.clone () ;que proporciona la conversión de tipos necesaria para asignar la referencia general Objectdevuelta clonea una referencia a un MyClassobjeto.
Una desventaja del diseño del clone()método es que el tipo de retorno clone()es Object, y necesita ser convertido explícitamente al tipo apropiado. Sin embargo, clone()es preferible sobrescribir para que devuelva el tipo apropiado y elimina la necesidad de conversión en el cliente (usando tipos de retorno covariantes , desde J2SE 5.0).
Otra desventaja es que a menudo no se puede acceder al clone()método en un tipo abstracto . La mayoría de las interfaces y clases abstractas en Java no especifican un clone()método público. Como resultado, a menudo el clone()método solo se puede usar si se conoce la clase real de un objeto, lo cual es contrario al principio de abstracción de usar el tipo más genérico posible. Por ejemplo, si se tiene una Listreferencia en Java, no se puede invocar clone()en esa referencia porque Listno especifica ningún método público clone(). Las implementaciones reales de Listcomo ArrayListy LinkedListgeneralmente tienen clone()sus propios métodos, pero es inconveniente y una mala abstracción tener que manejar el tipo de clase real de un objeto.
Alternativas
Existen alternativas clone(), como el uso de un constructor de copia (un constructor que acepta como parámetro otra instancia de la misma clase) o un método de fábrica . Estos métodos no siempre son adecuados cuando se desconoce de antemano el tipo concreto del objeto clonado. (Sin embargo, clone()tampoco suele ser adecuado por la misma razón, ya que la mayoría de las clases abstractas no implementan un clone()método público).
Asimismo, el uso de la serialización y deserialización es una alternativa al uso de clones.
Patrón Singleton
Al escribir una clase utilizando el patrón Singleton , solo puede existir una instancia de esa clase a la vez. Por lo tanto, no se debe permitir que la clase cree clones. Para evitar esto, se puede sobrescribir el clone()método utilizando el siguiente código:
public Object clone () throws CloneNotSupportedException { throw new CloneNotSupportedException (); }Esto solo es necesario si una superclase implementa un clone()método público, o para evitar que una subclase utilice clone()el método de esta clase para obtener una copia. Las clases no suelen heredar un clone()método público porque Objectno clone()lo tienen, por lo que normalmente no es necesario implementar explícitamente un método no funcional clone().
Jerarquía de clases
Para proporcionar un objeto clonable correctamente de cualquier tipo, el método clone() debe declararse correctamente e implementarse correctamente de acuerdo con la convención descrita en Object.clone().
1) Cada tipo que necesite ser clonado debe tener un método público clone() en su propia clase o un método clone() accesible públicamente en una de sus clases padre.
Ejemplo:
Para invocar el método clone() en varY1, que es de tipo Y, entonces Y o una clase padre de Y debe declarar un método clone() de acceso público. En este caso, es la clase padre X la que proporciona el método clone() público.
public class X implements Cloneable { public X clone () throws CloneNotSupportedException { return ( X ) super . clone (); } }clase pública Y extiende X { }clase pública Z extiende Y { }public class test1 { public void function () throws CloneNotSupportedException { Y varY1 = new Z (); Y varY2 = ( Y ) varY1 . clone (); } }2) Toda clase que implemente `clone()` debe llamar a `super.clone()` para obtener la referencia del objeto clonado. Si la clase también tiene referencias de objetos que deben clonarse (por ejemplo, al realizar una copia profunda), el método `clone()` debe realizar las modificaciones necesarias en el objeto antes de devolverlo. (Dado que `Object.clone()` devuelve una copia exacta del objeto original, los campos mutables, como colecciones y matrices, se compartirían entre el original y la copia, lo cual, en la mayoría de los casos, no es ni esperado ni deseado).
Ejemplo:
Dado que la clase Z contiene una referencia a un objeto, su método clone() también clona esa referencia para devolver una copia profunda del original.
public class X implements Cloneable { public X clone () throws CloneNotSupportedException { return ( X ) super . clone (); } }clase pública Y extiende X { }public class ObjectABC implements Cloneable { public ObjectABC clone () throws CloneNotSupportedException { return ( ObjectABC ) super . clone (); } }clase pública Z extiende Y { objeto privado ABC algúnABC ;public Z clone () throws CloneNotSupportedException { Z newZ = ( Z ) super . clone (); newZ . someABC = someABC . clone ();devolver newZ ; } }public class test1 { public void function () throws CloneNotSupportedException { Y varY1 = new Z (); Y varY2 = ( Y ) varY1 . clone (); } }Escollos
Si cada clase en una jerarquía implementa un clone()método, todas estas funciones se llamarán al clonarlas, lo que añade cierta sobrecarga. Tras muchas iteraciones, esta sobrecarga podría llegar a ser significativa.
En el caso de grafos de objetos complejos, la copia profunda también puede resultar problemática cuando existen referencias recursivas.
No siempre es apropiado tener varias copias del mismo objeto circulando. Si clone()los usuarios no comprenden completamente el propósito de una implementación específica, podría romper involuntariamente el paradigma de "un solo objeto, múltiples referencias".
Campos finales
En general, clone()es incompatible con finalcampos. Dado que clone()es esencialmente un constructor predeterminado (sin argumentos), es imposible asignar un finalcampo dentro de un clone()método; el resultado es un error de compilación. Si el valor del campo es un objeto inmutable, esto no supone ningún problema; basta con que el constructor copie la referencia y tanto el original como su clon compartirán el mismo objeto.
Pero cuando el valor es un objeto mutable, debe copiarse en profundidad. Una solución consiste en eliminar el finalmodificador del campo, renunciando a las ventajas que este confería.
Por esta razón, algunos programadores sugieren hacer que los objetos en la jerarquía sean serializables y crear copias serializando el objeto antiguo y luego creando un nuevo objeto a partir del flujo de bits resultante , lo que maneja correctamente los miembros de datos finales, pero es significativamente más lento. [ 1 ]
Como alternativa, se puede devolver un objeto completamente nuevo a partir de los campos del objeto actual, lo cual se puede hacer llamando primero al constructor y luego asignando campos no finales. Otro método alternativo consiste en formalizar la idea: crear un constructor de copia que reciba una instancia. De hecho, esto es lo que recomiendan algunos en lugar de usar clone. [ 2 ]
Referencias
- ↑ Miller, Dave (6 de agosto de 1999). "Java Tip 76: Una alternativa a la técnica de copia profunda" . JavaWorld . Recuperado el 14 de julio de 2020 .
- ↑ Clone() vs. Constructor Copy: ¿cuál se recomienda en Java ?, StackOverflow
Enlaces externos
- McManus, Eamonn (4 de abril de 2007). "Clonación de objetos Java mediante serialización" . Blog de Eamonn McManus . java.net. Archivado del original el 13 de agosto de 2010. Consultado el 16 de noviembre de 2010 .
- Bloch, Joshua (2008). Java eficaz: Guía de un lenguaje de programación . Serie Java (2.ª ed.). Addison-Wesley. ISBN 978-0-321-35668-0.
- "Evite la clonación" . Prácticas Java recopiladas . Hirondelle Systems. 2009. Consultado el 31 de julio de 2009 .
- "Object (Java Platform SE 6)" . Java Platform Standard Ed. 6. Sun Microsystems, Inc. 2008. Consultado el 31 de julio de 2009 .
- Roulo, Mark (1 de enero de 1999). "Cómo evitar trampas y sobrescribir correctamente los métodos de java.lang.Object" . JavaWorld . Consultado el 14 de julio de 2020 .- Cubre los aspectos básicos de la implementación del método de clonación.
- Tutorial de clonación en Java .
- Java (lenguaje de programación)
