Clonar objetos no Java

No Java o método clone() provê a criação de uma cópia similar ao objeto original. Em outras palavras o método copia campo a campo da classe clonada.
Para tornar uma classe clonável no Java são necessárias duas ações: implementar a interface “Cloneable” e sobrescrever o método clone(). É preciso ter em mente que ao clonar uma instância de uma classe os atributos primitivos (int, float, double, char, byte, etc…) são uma nova cópia do objeto original, já o atributo tipado (objeto de classe) acoplado terá por padrão copia da referencia original, ou seja, se mudamos o valor no atributo do objeto acoplado, todos que enxergam a mesma referencia serão afetado com a alteração.
Vamos ao primeiro exemplo de clonagem simples demonstrando a questão do atributo referenciado:
Classe Main

package mydelirium;
/**
 *
 * @author Ricardo Spinoza
 */
public class MyDelirium {

    /**
     * @param args the command line arguments
     * @throws java.lang.CloneNotSupportedException
     */
    public static void main(String[] args) throws CloneNotSupportedException{
        
        Dog dogOriginal = new Dog();        
        dogOriginal.setName("Ted");
        dogOriginal.setRace("Pitbull");
        
        Person personOriginal = new Person();
        personOriginal.setAge(24);
        personOriginal.setName("Jonnhy");
        personOriginal.setDogFriend(dogOriginal);
        
        Person personClone = (Person) personOriginal.clone();        
        
        System.out.println("original >> name: " + personOriginal.getName() + " age:" +personOriginal.getAge()+ " dog name:" + personOriginal.getDogFriend().getName()+ " dog race:" + personOriginal.getDogFriend().getRace());
        System.out.println("cloned >>>> name: " + personClone.getName() + " age:" +personClone.getAge()+ " dog name:" + personClone.getDogFriend().getName()+ " dog race:" + personClone.getDogFriend().getRace());
        //System.out.println("orginal equals cloned: " + personOriginal.equals(personClone));
        
        System.out.println("Dog é uma referencia comum entre os dois objetos (original e clonado)? "
                + personOriginal.getDogFriend().equals(personClone.getDogFriend()));
        
        System.out.println();
        
        personOriginal.getDogFriend().setName("Xena");
        personOriginal.getDogFriend().setRace("Yourshire Terrier");
        personOriginal.setAge(33);
        
        System.out.println("Ou seja, ao mudar o objeto Dog teremos: \n");
        
        System.out.println("original >> name: " + personOriginal.getName() + " age:" +personOriginal.getAge()+ " dog name:" + personOriginal.getDogFriend().getName()+ " dog race:" + personOriginal.getDogFriend().getRace());
        System.out.println("cloned >>>> name: " + personClone.getName() + " age:" +personClone.getAge()+ " dog name:" + personClone.getDogFriend().getName()+ " dog race:" + personClone.getDogFriend().getRace());                
    } 
}

Classe Person

package mydelirium;

/**
 *
 * @author Ricardo.Pereira
 */
public class Person implements Cloneable{
    
    private int age;    
    private String name;
    
    private Dog dogFriend;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Dog getDogFriend() {
        return dogFriend;
    }

    public void setDogFriend(Dog dogFriend) {
        this.dogFriend = dogFriend;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); 
    }
    
}

Classe Dog

package mydelirium;

/**
 *
 * @author Ricardo.Pereira
 */
public class Dog{
 
    private String name;
    private String race;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRace() {
        return race;
    }

    public void setRace(String race) {
        this.race = race;
    }
}

A saída do código será:

<pre>original >> name: Jonnhy age:24 dog name:Ted dog race:Pitbull
cloned >>>> name: Jonnhy age:24 dog name:Ted dog race:Pitbull
Dog é uma referencia comum entre os dois objetos (original e clonado)? true
Ou seja, ao mudar o objeto Dog teremos: 
original >> name: Jonnhy age:33 dog name:Xena dog race:Yourshire Terrier
cloned >>>> name: Jonnhy age:24 dog name:Xena dog race:Yourshire Terrier

No exemplo clonamos o objeto original que possui o atributo dogFriend (da classe Dog) o qual possui a mesma referencia nos objetos personOriginal e personClone. Observe que qualquer mudança de valor no atributo dogFriend o objeto original e clonado são afetados.
Para resolver o problema da referencia neste cenário precisaremos adicionar o método clone() na nossa classe Dog (obviamente implementar a interface Clonable) e alterar o método clone() definido na nossa classe Person para clonar o objeto Dog e assim isolar as referências de cada objeto.
Segue as mudanças no segundo exemplo:
Classe Main

package mydelirium;
/**
 *
 * @author Ricardo Spinoza
 */
public class MyDelirium {

    /**
     * @param args the command line arguments
     * @throws java.lang.CloneNotSupportedException
     */
    public static void main(String[] args) throws CloneNotSupportedException{
        
        Dog dogOriginal = new Dog();        
        dogOriginal.setName("Ted");
        dogOriginal.setRace("Pitbull");
        
        Person personOriginal = new Person();
        personOriginal.setAge(24);
        personOriginal.setName("Jonnhy");
        personOriginal.setDogFriend(dogOriginal);        
        Person personClone = (Person) personOriginal.clone();                
        System.out.println("original >> name: " + personOriginal.getName() + " age:" +personOriginal.getAge()+ " dog name:" + personOriginal.getDogFriend().getName()+ " dog race:" + personOriginal.getDogFriend().getRace());
        System.out.println("cloned >>>> name: " + personClone.getName() + " age:" +personClone.getAge()+ " dog name:" + personClone.getDogFriend().getName()+ " dog race:" + personClone.getDogFriend().getRace());        
        System.out.println("Dog é uma referencia comum entre os dois objetos (original e clonado)? "
                + personOriginal.getDogFriend().equals(personClone.getDogFriend()));
        
        System.out.println();
        
        personOriginal.getDogFriend().setName("Xena");
        personOriginal.getDogFriend().setRace("Yourshire Terrier");
        personOriginal.setAge(33);
        
        System.out.println("Ao mudar o objeto Dog no objeto original teremos: \n");
        
        System.out.println("original >> name: " + personOriginal.getName() + " age:" +personOriginal.getAge()+ " dog name:" + personOriginal.getDogFriend().getName()+ " dog race:" + personOriginal.getDogFriend().getRace());
        System.out.println("cloned >>>> name: " + personClone.getName() + " age:" +personClone.getAge()+ " dog name:" + personClone.getDogFriend().getName()+ " dog race:" + personClone.getDogFriend().getRace());
                
    }
    
}

Classe Person

package mydelirium;

/**
 *
 * @author Ricardo.Pereira
 */
public class Person implements Cloneable{
    
    private int age;    
    private String name;
    
    private Dog dogFriend;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Dog getDogFriend() {
        return dogFriend;
    }

    public void setDogFriend(Dog dogFriend) {
        this.dogFriend = dogFriend;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person)super.clone();
        //faz o clone do objeto dogFriend
        cloned.setDogFriend((Dog)cloned.getDogFriend().clone());
        return cloned;
    }
    
}

Classe Dog

package mydelirium;

/**
 *
 * @author Ricardo.Pereira
 */
public class Dog implements Cloneable{
 
    private String name;
    private String race;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getRace() {
        return race;
    }

    public void setRace(String race) {
        this.race = race;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

A saída do código será:

original >> name: Jonnhy age:24 dog name:Ted dog race:Pitbull
cloned >>>> name: Jonnhy age:24 dog name:Ted dog race:Pitbull
Dog é uma referencia comum entre os dois objetos (original e clonado)? false

Ao mudar o objeto Dog no objeto original teremos: 

original >> name: Jonnhy age:33 dog name:Xena dog race:Yourshire Terrier
cloned >>>> name: Jonnhy age:24 dog name:Ted dog race:Pitbull

Por fim, a clonagem de objetos facilita criar cópias de objeto, porém é preciso ter alguns cuidados com relação a referencia de objetos tipados entre o objeto original e o clonado.

Dúvidas, criticas, sugestões, por favor escreva 🙂
Referencia:

API Java Object clone() http://http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#clone()
Leia mais em A guide to object cloning in java <http://howtodoinjava.com/2012/11/08/a-guide-to-object-cloning-in-java/&gt;

Anúncios

Deixe um comentário

Arquivado em Java

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s