La copia de objetos en Java es el proceso de crear un nuevo objeto con el mismo estado que una instancia existente. Históricamente, la copia se volvió relevante desde el inicio del desarrollo de Java, cuando surgió la necesidad de duplicar objetos sin recrearlos manualmente. Java en sí no proporciona un método universal para copiar objetos, sino que utiliza el contrato clone() y el patrón del constructor de copia.
El método clone() fue introducido en la interfaz Cloneable para ofrecer un mecanismo estándar de clonación de objetos, sin embargo, su implementación presenta muchos matices y a menudo lleva a la aparición de errores.
El principal problema es la falta de una verdadera copia profunda "fuera de la caja" y la posibilidad de comportamiento impredecible al realizar una clonación superficial (shallow copy). El constructor de copia permite implementar tanto la copia superficial como la profunda, pero requiere una implementación explícita.
Para una copia segura y correcta de objetos complejos, es preferible usar un constructor de copia o un método de fábrica, mientras que clone() debe aplicarse con precaución: solo si el objeto realmente se ajusta a este contrato.
Ejemplo de código:
// Clonación superficial mediante clone() public class Address implements Cloneable { String city; public Address(String city) { this.city = city; } @Override public Address clone() throws CloneNotSupportedException { return (Address) super.clone(); } } public class Person implements Cloneable { String name; Address address; public Person(String name, Address address) { this.name = name; this.address = address; } @Override public Person clone() throws CloneNotSupportedException { Person copy = (Person) super.clone(); copy.address = address.clone(); // copia profunda de address return copy; } } // Constructor de copia public class Person { String name; Address address; public Person(Person other) { this.name = other.name; this.address = new Address(other.address.city); } }
Características clave:
clone() requiere la implementación de la interfaz Cloneable y la sobreescritura del método clone.Pregunta 1: ¿Qué sucede si la clase no implementa Cloneable al llamar a clone()?
clone() lanzará una CloneNotSupportedException si el objeto no implementa la interfaz Cloneable.
Pregunta 2: ¿Es la clonación a través de clone() profunda por defecto?
No, por defecto se implementa solo la clonación superficial, todas las referencias a objetos se copian tal como están.
Pregunta 3: ¿Se puede prescindir del constructor de copia para la clonación profunda?
No, si se quiere controlar el proceso, es necesario especificar explícitamente la copia de los objetos anidados a través del constructor o manualmente en clone().
Un desarrollador usó clone() estándar en la clase Order con un campo List<Product>. Como resultado, al modificar la lista de productos en la copia, también se modificaron los productos en el original, lo que provocó un error.
Ventajas:
Desventajas:
En la empresa se implementó un constructor de copia para todas las entidades del dominio, cubriendo adicionalmente el proceso de clonación para objetos anidados complejos con pruebas.
Ventajas:
Desventajas: