org.gvsig.tools.lang
Herramientas relacionadas con el paquete java.lang
Clonación de objetos --------------------- En algunas ocasiones tenemos la necesidad de poder realizar copias de ciertos objetos, o como se suele decir también, de poder clonarlos. Supongamos que tenemos la siguiente una jerarquía de objetos, a modo de ejemplo: .. figure:: images/clonacion-ejemplo1.png :align: center El código de estas clases sería algo como: .. code-block:: java public class Dependency { private int value; // followed by a getter and setter for the value attribute ... } public class Parent { private String text; private Date creation; // followed by getters and setters for the text and creation attributes ... } public class Child extends Parent { private float price; private Dependency dependency; // followed by getters and setters for the price and dependencty attributes ... } En este ejemplo, podríamos tener una instancia de la clase *Child*, y el objetivo sería obtener una copia. Existen diversas formas habituales de implementarlo, entre las que se incluyen: constructor de copia, factoría, etc. Una de las más habituales es que el propio objeto provea de un método que nos devuelva una copia del mismo. De hecho, en Java suele emplearse el método *clone()* y el interfaz *Cloneable*. El interfaz `java.lang.Cloneable`__ es un interfaz vacío que sirve para indicar al método `clone()`__ de `java.lang.Object`__ que se puede hacer una copia de un objeto a partir de sus campos. Sin embargo, como se indica en el javadoc del propio interfaz *java.lang.Cloneable*, por convención, clases que implementen dicho interfaz, deberían sobreescribir el método *clone()*, que es *protected* en *Object*, por otro que sea *public*. Para dejar más claro este esquema, se ha definido un interfaz *org.gvsig.tools.lang.Cloneable* que extiende a *java.lang.Cloneable* y define, a su vez, el método *clone()* como público. Así, cuando queramos que un objeto soporte la creación de copias de sí mismo, implementaremos el interfaz *org.gvsig.tools.lang.Cloneable*, e implementaremos el método *clone()*. ¿Cómo implementamos el método *clone()*? Por lo general bastará con llamar al *clone()* de la clase *Object*. Sobre el ejemplo anterior, quedaría así: .. code-block:: java public class Dependency implements org.gvsig.tools.lang.Cloneable { private int value; // followed by a getter and setter for the value attribute ... public Object clone() throws CloneNotSupportedException { return super.clone(); } } public class Parent implements org.gvsig.tools.lang.Cloneable { private String text; private Date creation; // followed by getters and setters for the text and creation attributes ... public Object clone() throws CloneNotSupportedException { return super.clone(); } } public class Child extends Parent implements org.gvsig.tools.lang.Cloneable { private float price; private Dependency dependency; // followed by getters and setters for the price and dependencty attributes ... public Object clone() throws CloneNotSupportedException { return super.clone(); } } Con esto ya tendría una implementación de clonación básica. Desde código, bastaría con hacer lo siguiente para obtener un clon de un objeto *Child*: .. code-block:: java Child child = new Child(); ... Child childClone = (Child)child.clone(); Sin embargo, tenemos algunos problemas debido al funcionamiento de la clonación base y cómo funciona el modelo de ejemplo: 1) El valor del atributo *Parent.creation* no queremos que sea el mismo que el de la clase clonada, ya que queremos que tenga la fecha de creación del objeto. Para resolverlo bastará con modificar la implementación del método *Parent.clone()* para que asigne la fecha actual en el momento de realizar la clonación: .. code-block:: java public class Parent implements org.gvsig.tools.lang.Cloneable { private String text; private Date creation; // followed by getters and setters for the text and creation attributes ... public Object clone() throws CloneNotSupportedException { Parent clone = (Parent)super.clone(); clone.creation = new Date(); return clone; } } 2) La lógica del método *clone()* de la clase Object habrá copiado todos los valores desde nuestra instancia a la clonada por asignación (*shallow copy*), es decir: - Si se trata de un tipo básico, se copia su valor. - Si se trata de un objeto, se copia su referencia. Con esto, la referencia la objeto de tipo *Dependency* será la misma en ambos objetos, el original y el clonado. Es decir, los dos apuntarán a la misma instancia de *Dependency*: .. figure:: images/clonacion-ejemplo-instancias1.png :align: center Dado que *dependency* es un objeto mutable y podemos modificar su estado, no podemos compartir la misma instancia entre el objeto original y el clonado, ya que modificaciones desde uno afectarían al otro. Deberemos crear una instancia propia de *Dependency* para el objeto clonado, que sea una copia de la del objeto original: .. figure:: images/clonacion-ejemplo-instancias2.png :align: center A nivel de implementación, la clase *Child* quedaría como sigue aprovechando que *Dependency* es, a su vez, *Cloneable*: .. code-block:: java public class Child extends Parent implements org.gvsig.tools.lang.Cloneable { private float price; private Dependency dependency; // followed by getters and setters for the price and dependencty attributes ... public Object clone() throws CloneNotSupportedException { Child clone = (Child) super.clone(); clone.dependency = (Dependency)dependency.clone(); return clone; } } Pasará lo mismo con otros atributos de tipo objeto, como es el caso del atributo *Parent.text*. Sin embargo, el atributo *text* es de tipo *String*, la cuál tiene la particularidad de ser inmutable, por lo que podemos compartir la referencia sin problemas. Resumiendo, a la hora de implementar la clonación de una clase, seguiremos los siguientes pasos: - Implementaremos el interfaz *org.gvsig.tools.lang.Cloneable*. - Implementaremos el método *clone()*. - A la hora de crear el nuevo objeto copia a devolver, no usaremos *new*, sino que invocaremos al *clone()* del *super*: - Adaptaremos la copia obtenida en el paso anterior, según la lógica de nuestra clase, fijándonos sólo en los atributos que define nuestra clase, y no en los que puedan definir clases padre. Tendremos distintos casos de atributos, según su tipo: - Tipo básico: ya tendremos el valor copiado de nuestro atributo al del objeto clonado. Sólo tendremos que hacer algo si no queremos que ese valor se copie o tenga un valor distinto. - Tipo objeto inmutable: es decir, aquellos que una vez contruidos, ya no puede cambiarse su estado, como por ejemplo la clase *String*, *Integer*, etc. En este caso, no será necesario hacer nada ya que, aunque tendremos en los dos objetos (el original y el clonado) una referencia a un mismo objeto, éste no puede cambiar su estado, por lo que no nos afectará lo que hagamos en un objeto sobre el otro para este atributo. - Tipo objeto mutable: en este caso, a no ser que nos interese así, porque se trate de un dato común, o por ejemplo una referencia a un servicio, deberemos clonar el valor del atributo. Se puede ver más información sobre clonación en el siguiente tip del `Java Developer Connection sm(JDC) Tech Tips, March 6, 2001`__ __ http://java.sun.com/javase/6/docs/api/java/lang/Cloneable.html __ http://java.sun.com/javase/6/docs/api/java/lang/Object.html#clone() __ http://java.sun.com/javase/6/docs/api/java/lang/Object.html __ http://java.sun.com/developer/JDCTechTips/2001/tt0306.html