Personal tools
You are here: Home gvSIG Projects gvSIG Desktop Documentation Developers documentation org.gvsig.tools 2.1.0 org.gvsig.tools.lang
gvSIG Desktop
gvSIG Desktop

Cached time 11/21/13 11:24:39 Clear cache and reload

 
Document Actions

org.gvsig.tools.lang

by Cèsar Ordiñana last modified 2010-07-13 15:48

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:

images/clonacion-ejemplo1.png

El código de estas clases sería algo como:

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í:

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:

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:

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;
     }
}
  1. 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:

images/clonacion-ejemplo-instancias1.png

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:

images/clonacion-ejemplo-instancias2.png

A nivel de implementación, la clase Child quedaría como sigue aprovechando que Dependency es, a su vez, Cloneable:

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


Powered by Plone CMS, the Open Source Content Management System

This site conforms to the following standards: