Como eu posso copiar um objeto sem modificar o original?!

Pessoal, estou com uma dúvida…

Bem, eu quero copiar um objeto para outro do mesmo tipo, sem ser por referência… Eu sei que quando eu declaro um novo objeto e faço ele receber, por exemplo, outro objeto, o que eu estou fazendo é com que este objeto aponte para a mesma região de memória do Original… O que eu quero é que um novo objeto, só que com os mesmos valores que o anterior seja criado…

Ouvi falar do método Clone(); Mas não consegui implementá-lo, por exemplo eu tenho uma classe Aluno:

Fiz assim:

public class Aluno extends org.hibernate.util.Cloneable{
private int idade;
private String Nome;

public Aluno(int idade, String Nome) {
  this.idade = idade;
  this.Nome = Nome;
}
public Aluno(){

}

public String getNome() {
    return Nome;
}

public Object clone() throws CloneNotSupportedException{
    return super.clone();
}


public int getIdade() {
    return idade;
}

public void setIdade(int idade){
    this.idade = idade;
}

}

Mas dá uns erros quando eu tento utilizá-lo:

Exception in thread “main” java.lang.CloneNotSupportedException: teste.Aluno
20
at java.lang.Object.clone(Native Method)
30
30
at teste.Aluno.clone(Aluno.java:29)
30
at teste.Turma.getAluno(Turma.java:22)
at teste.Main.Teste2(Main.java:34)
at teste.Main.main(Main.java:61)
Java Result: 1

Alguém tem algum exemplo de utilização deste, ou outro método para fazer isso que eu quero!!

Olá amigo,
primeiramente use a tag code entre [] para colocar seus codigos de forma mais legivel no forum.
Segundo n precisa extender sua classe para org.hibernate.util.Cloneable.

Basta criar um metodo assim

public Aluno clone(Aluno a){
   Aluno novo = new Aluno();
   novo.setNome(a.getNome());
   novo.setSegundoNome(a.getSegundoNome)); 
//E assim por diante para todas as propriedades.
}

pronto metodo clone implementado.

A assinatura correta do método clone é:

@Override public Aluno clone() { Aluno novo = new Aluno(); novo.setNome(getNome()); novo.setSegundoNome(getSegundoNome()); }

Você também tem que alterar a declação da sua classe Aluno para que ela implemente Cloneable:

public class Aluno implements Cloneable

A vantagem desse método é que o super() de object automaticamente copia todas as variáveis do objeto. No seu caso, seria o objeto todo:

@Override public Aluno clone() { return (Aluno) super.clone(); }

Você só tem que tomar cuidado que essa chamada ao super não copiará os objetos relacionados ao seu objeto, apenas suas referências. Ou seja, se seu aluno tiver uma propriedade “Escola”, e essa também tiver que ser copiada, será necessário chamar o clone() para ela também.

public Aluno clone() { Aluno outro = (Aluno) super.clone(); outro.escola = this.escola.clone(); }

Caso contrário, ambos alunos apontarão para a mesma escola.

Vlw Vini esqueci dos outros detalhes

Só uma coisinha, a assinatura é realmente “public Object clone() throws CloneNotSupportedException”.

http://java.sun.com/javase/6/docs/api/java/lang/Object.html#clone()

class Aluno implements Cloneable {
    public Aluno (final String nome_, final int id_) { nome = nome_; id = id_; }
    @Override 
    public Object clone() throws CloneNotSupportedException {
        Aluno ret = new Aluno(nome, id);
        return ret;
        // Como o Vini disse, é possível usar neste caso (em que se pode usar um "shallow cloning"
        // return super.clone(); que é mais fácil de usar.
    }
    public void setId (final int id_) { id = id_; }
    public String toString() { return nome + "," + id; }
    private String nome; private int id;
}

class Teste {
    public static void main(String[] args) throws Exception {
        Aluno a = new Aluno ("José Arimatéia", 1234);
        Aluno b = (Aluno) a.clone();
        System.out.println ("Os dois objetos contém os mesmos valores");
        System.out.println (a);
        System.out.println (b);
        a.setId (1235);
        b.setId (1236);
        System.out.println ("Veja como os objetos são independentes entre si.");
        System.out.println (a);
        System.out.println (b);
    }
}

Bem, pelo que entendi reescrevendo esse método clone, basicamente eu iria copiar todos os atributos(variáveis do objeto original) para o objeto recém criado, certo? Porém se ele tiver um Objeto como Escola, eu não poderia simplesmente utilizar o “getEscola”, pois assim o novo objeto, recém criado, iria apenas referenciar escola certo??! No caso eu teria que ter um clone em Escola também…

Vini, o que eu não entendi é que se esse método “super.clone()” ele já trata essa questão de ter outro objetos, ou eu também teria que fazer a mesma coisa acima??"

Att,

vai la em www.apache.org
procure por commons e depois por BeanUtils, baixe o jar e depois e so fazer BeansUtils.copyProperties(dest,org);

Lendo a documentação de Object.clone, você vai ver que ele faz uma “shallow copy”, ou seja, ele simplesmente copia os valores dos atributos, um por um. Ele não “clona” cada atributo recursivamente. Portanto, se sua classe Aluno fosse assim:

class Aluno implements Cloneable {
    private Professor professorMatematica;
    private Professor professorIngles;
    private int id;
    @Override public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

e você fizer um clone de um aluno, não irá clonar os seus professores, só irá copiar as referências.

[quote=thingol]Só uma coisinha, a assinatura é realmente “public Object clone() throws CloneNotSupportedException”.

http://java.sun.com/javase/6/docs/api/java/lang/Object.html#clone()[/quote]

Sim Thingol. Porém, a partir do Java 5, você tem os covariant return types, o que permite que seu método clone() retorne o tipo da sua própria classe.
E uma subclasse pode omitir uma exceção da superclasse (é até recomendável fazer isso, nesse caso, para evitar apurrinhação desnecessária).

Por isso, a assinatura que eu coloquei ali é válida.

Esse código continua válido:

[code]
class Aluno implements Cloneable {
public Aluno (final String nome_, final int id_) { nome = nome_; id = id_; }
//Note que também é valido. Aluno é um filho de Object, logo é
//possível usar o covariant return type. Também é possível otimir a
//exception, já que isso não fere o contrato do clone
@Override
public Aluno clone() {
Aluno ret = new Aluno(nome, id);
return ret;
}
public void setId (final int id_) { id = id_; }

@Override
public String toString() { return nome + "," + id; }
private String nome; private int id;

}

public class Teste {
//Omitir a exception evita que tenhamos que trata-la
public static void main(String[] args) {
Aluno a = new Aluno (“José Arimatéia”, 1234);
//O covariant return evita o cast
Aluno b = a.clone();
System.out.println (“Os dois objetos contém os mesmos valores”);
System.out.println (a);
System.out.println (b);
a.setId (1235);
b.setId (1236);
System.out.println (“Veja como os objetos são independentes entre si.”);
System.out.println (a);
System.out.println (b);
}
}[/code]

Isso, no método clone você precisa copiar tudo que o objeto que está sendo clonado tem. O super.clone() faz essa cópia para você. Porém, no caso de objetos, ele só copiará as referências. Se você quiser copiar o objeto também, tem que chamar clone sobre esse objeto.

[quote=thingol]Só uma coisinha, a assinatura é realmente “public Object clone() throws CloneNotSupportedException”.

http://java.sun.com/javase/6/docs/api/java/lang/Object.html#clone()
[/quote]

Sim, mas a partir do java 5 o retorno dos métodos é covariante o que significa que vc pode escrever que o método retorna qualquer classe que seja igual ou filha da classe original. Como clone declara object como retorno isso significa que vc pode declarar qq objeto como retorno. É util retornar a propria classe.

[quote=rafaelob]Pessoal, estou com uma dúvida…

Bem, eu quero copiar um objeto para outro do mesmo tipo, sem ser por referência… Eu sei que quando eu declaro um novo objeto e faço ele receber, por exemplo, outro objeto, o que eu estou fazendo é com que este objeto aponte para a mesma região de memória do Original… O que eu quero é que um novo objeto, só que com os mesmos valores que o anterior seja criado…

Ouvi falar do método Clone(); Mas não consegui implementá-lo, por exemplo eu tenho uma classe Aluno:
[/quote]

Esqueça clone. É demasiado problemático (leia Effective Java para entender pq)

Para criar uma copia vc apenas precisa de um construtor de copia. Esta tecnica é mais limpa que clone() e é usada na api de collections, por exemplo.

A ideia é simples, crie um construtor que recebe um objeto do mesmo tipo.


public class Aluno {

    String nome;
    String segundoNome;

    public Aluno (){} // construtor normal

   public Aluno (Aluno other){
     this.nome = other.nome;
    this.segundoNome = other.segundoNome;
 }

}

uso


Aluno aluno = new Aluno ();

aluno.setNome("Meu nome");

Aluno copia = new Aluno(aluno);

Esse último método mostrado, também copia os objetos que existam na classe Aluno? sem apenas passar a referência??

Att,

Se eu tivesse um objeto Escola, dentro dessa classe aluno, ele copiaria o valor do objeto, ou passaria a referência dele nessa cópia?

Só se fizesse sentido. Caso contrário, teria que fazer um método desse para o objeto, e chama-lo também.

Ele passaria a referencia. Isso é o comportamento esperado em 90% dos casos.
Se vc quser duplicar algums dos objetos vc faria esse duplicação explicitamente. por exemplo, para duplicar a escola:


public class Aluno {

   Stirng name;
   Escola escola;

   public Aluno(Aluno other){
         this.name= other.name;
         this.escola  = new Escola(other.escola);
  }

}

Só que esta tecnica, mesmo funcionando tem vários problemas. Uma melhor forma seria assim


public class Aluno {

   Stirng name;
   Escola escola;

   private Aluno(Aluno other){
         this.name= other.name;
         this.escola  = other.escola.duplicate();
  }
   
   public Aluno duplicate(){
         return new Aluno(this);
  }
}

Veja que isto é muito semelhante a clone().
Na realidade o construtor de copia é uma forma de implementa clone.


public class Aluno implements Clonable{

   Stirng name;
   Escola escola;

   private Aluno(Aluno other){
         this.name= other.name;
         this.escola  = other.escola.duplicate();
  }
   
   public Aluno clone(){
         return new Aluno(this);
  }
}

Obrigado!!!

Vc pode usar tb a Serialização do Java para isso. Apesar de mais lento, ele já trata o problema de referencias e não precisa ficar implementando vários métodos clone espalhados (as vezes vc nem tem o codigo fonte…)
No link abaixo tem uma implementação simples da técnica e tem tb uma implementação otimizada.
http://javatechniques.com/blog/faster-deep-copies-of-java-objects/
Abraços!

[quote=pedro.cavalero]Vc pode usar tb a Serialização do Java para isso. Apesar de mais lento, ele já trata o problema de referencias e não precisa ficar implementando vários métodos clone espalhados (as vezes vc nem tem o codigo fonte…)
No link abaixo tem uma implementação simples da técnica e tem tb uma implementação otimizada.
http://javatechniques.com/blog/faster-deep-copies-of-java-objects/
[/quote]

Essa tecnica é furada em vários niveis e sofre os mesmo problema de clone().
Não use essa tecnica nunca. Leia effective java para saber porquê.

Bem, tem muitos contras se vc usar serialização irrestritamente. Mas dizer pra não usar nunca nem o Effective Java fala. É importante conhecer os problemas q ele tem pra saber se o seu caso não vai ser afetado. No caso de simples cópias de objetos (não pra persistência) nos casos menos problemáticos (herança entre outros) pode ser uma opção.