Criar Metodo clone() Generico

29 respostas
colored

Bom to querendo criar um metodo clone() so q generico… pq pelo jeito vo precisar utilizar em varios lugares…
e n keria ter q colocar em cada POJO esse metodo…
Já queria tratar em lugar soh… exception, casting… enfim…

Tentei mas pra variar neh n deu certo kkk…

Criei essa Classe q vai ser de onde vou chamar…

public class Clone<T> extends Cloneable {
	
	private final Class<T> classe;
	
	public Clone(Class<T> classe) {
		this.classe = classe;
	}
	
	@SuppressWarnings("unchecked")
	public T clone() throws CloneNotSupportedException{
		return (T) super.clone();
	}
	
}

pra poder criar assim mais o menos

Pessoa p = new Pessoa();
		p.setNome("José");
		Pessoa person = new Clone<Pessoa>(Pessoa.class).clone();
		System.out.println(person.getNome());

Mas eu Estou achando que o problema está na hora d dar o super
mas eu n tenho ideia d como fazer isso funcionar…

ai Gera esse Erro

Exception in thread "main" java.lang.CloneNotSupportedException: Unimed.Util.Clone
	at java.lang.Object.clone(Native Method)
	at Unimed.Util.Clone.clone(Clone.java:22)
	at Unimed.Teste.Teste.main(Teste.java:21)

Será q Alguem pode Me Ajudar to perdidao…
MUito Obrigado :smiley:

29 Respostas

Marck

Olá,

Cara, fiz algumas alterações aqui e até funcionou. Funcionou para clonar a class Clone. Porem, o objeto que tem dentro dela, o Pessoa, nao clona.

class Clone<T> implements Cloneable {

    T t;

    public Clone(T t) {
        this.t = t;
    }

    public T getT()
    {
        return t;
    }

    @SuppressWarnings("unchecked")
    public Clone<T> clone() throws CloneNotSupportedException{
        return (Clone<T>) super.clone();
    }
}

Dei uma olhada na net e tem algo como DeepCopy.
Outra alternativa poderia ser fazer com Reflection.

att,

colored

Então ai q tá neh… o problema acho q tá no super
ele executa na classe q ele tá…

Marck

Não é isso. O sem o super entraria em loop.
O problema ai é que Clone que é clonavel, e não o seu objeto.

Pesquisei e achei uma alternativa. Funcionou:

import java.io.*;
import java.util.*;
import java.awt.*;
public class Cloner
{
   // so that nobody can accidentally create an ObjectCloner object
   private Cloner(){}
   // returns a deep copy of an object
   static public Object deepCopy(Object oldObj) throws Exception
   {
      ObjectOutputStream oos = null;
      ObjectInputStream ois = null;
      try
      {
         ByteArrayOutputStream bos =
               new ByteArrayOutputStream(); // A
         oos = new ObjectOutputStream(bos); // B
         // serialize and pass the object
         oos.writeObject(oldObj);   // C
         oos.flush();               // D
         ByteArrayInputStream bin =
               new ByteArrayInputStream(bos.toByteArray()); // E
         ois = new ObjectInputStream(bin);                  // F
         // return the new object
         return ois.readObject(); // G
      }
      catch(Exception e)
      {
         System.out.println("Exception in ObjectCloner = " + e);
         throw(e);
      }
      finally
      {
         oos.close();
         ois.close();
      }
   }

}

class Test{

    public static void main(String[] a) {

        Pessoa p = new Pessoa("Original");

        try{
        Pessoa p1 = (Pessoa) Cloner.deepCopy(p);
        p1.n = "Clone";
        System.out.println(p.n);
        System.out.println(p1.n);
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
}

class Pessoa implements Serializable
{
    String n;

    public Pessoa(String name)
    {
        n = name;
    }
}

…porém seu objeto tem que ser Serializavel.

Artigo completo -> http://www.javaworld.com/javaworld/javatips/jw-javatip76.html?page=2

colored

Eu Antes de implementar uma solução na parte de um dao generico consegui escapar d por serializable nas classes…
o qto isso pode me prejudicar no meio do projeto caso eu coloke isso nos POJO’s???

Marck

Então cara, eu não vejo problema em ter herança com Serializable, ainda mais que trata-se de uma Interface marcadora, ou seja, voce não vai perder usando o extends.
Mas já que voce quer fugir disso, a ultima opção que consigo imaginar é fazer o clone com Reflection.

Este exemplo eu não testei, mas dá uma olhada:

http://www.stupidjavatricks.com/?p=26

Marck

OPá!!!
Testei e funcionou!

public static Object clone(Object o)
{
  Object clone = null;
 
  try
  {
     clone = o.getClass().newInstance();
  }
  catch (InstantiationException e)
  {
     e.printStackTrace();
  }
  catch (IllegalAccessException e)
  {
     e.printStackTrace();
  }
 
  // Walk up the superclass hierarchy
  for (Class obj = o.getClass();
    !obj.equals(Object.class);
    obj = obj.getSuperclass())
  {
    Field[] fields = obj.getDeclaredFields();
    for (int i = 0; i < fields.length; i++)
    {
      fields[i].setAccessible(true);
      try
      {
        // for each class/suerclass, copy all fields
        // from this object to the clone
        fields[i].set(clone, fields[i].get(o));
      }
      catch (IllegalArgumentException e){}
      catch (IllegalAccessException e){}
    }
  }
  return clone;
}
colored

kkkkkkkkkkkk… Boa…
Então pq eu achei um topico no Guj… de um cara q tava fazendo com ObjectOutputStream
e talz parecido com akele exemplo q vc me mando…
e tava recriminando talz e utilizava muita memoria com perigo de estourar sei lá…

Mas vc acha q esse com Reflection eh melhor??

colored

valeo acho q ja da pra começar será q com reflection tem como eu pegar as variaveis de instancias do objeto e clonar tb no mesmo metodo???

ViniGodoy

Acho melhor você adicionar um método em cada POJO. Se seus pojos tiverem um ancestral comum (e não forem tão pojos assim), você poderia colocar o clone lá também.

Afinal, o método é simples assim:

public Pessoa clone() { return (Pessoa) super.clone(); }

Não precisa lançar a exception, já que seu objeto efetivamente é clonável.

E já faz quem programa parar para pensar se só isso é suficiente. Lembre-se que pode ser necessário fazer deep copy de objetos relacionados, e que nada impede que objetos tenham referências circulares.

Geralmente é uma má idéia tratar clonagem ou serialização de maneira genérica e despreocupada no sistema.

colored

ViniGodoy…
Mas mesmo assim tenho q dar um throws no metodo neh…
esse era um detalhe q eu queria evitar…

Tipo Gostaria de tratar tudo isso em 1 lugar soh… neh…

ViniGodoy

Não, não tem que dar throws no método.

ViniGodoy

Como vc vai tratar em um lugar só, se cada objeto é diferente?

Você pode mesmo usar reflexão, mas ela insere outros problemas:

  1. Os erros de reflexão ocorrem em Runtime;
  2. Ela não pensará se a cópia que está fazendo faz sentido ou não;
  3. Você deverá tratar manualmente casos de referências circulares;

Ainda acho melhor escrever 3 linhas a mais por POJO (nem é muita coisa), e manter seu programa seguro na compilação e uma obrigação do seu programador pensar no que está fazendo.

A única coisa chata é que realmente você ainda precisará fazer “implements Cloneable” no topo da sua classe.

colored

ueh então to fazendo algo de errado…

public Usuario clone(){
		return (Usuario) super.clone();
	}

Por exemplo… ele marca o super.clone com “Unhandled exception type CloneNotSupportedException”

ViniGodoy

É verdade, vc terá que fazer um try catch.

public Usuario clone(){ try { return (Usuario) super.clone(); } catch (CloneNotSupported e) {} }

Ainda é melhor que o throws, pois aí quem usa a classe poderá fazer:

Usuario u2 = u1.clone();

Sem ter que capturar exception alguma.

colored

Estranho eu tava fazendo um teste aki… e não implementei Cloneable… e mesmo assim funcionou…
POr que Será?

Marck

No meu sistema, os Pojo’s tem um ancestral em comum, que é o Entity. Este implementa Serializable. Assim, todos os Pojo’s são clonáveis.
E é como disse: não acho que seja um problema implementar uma interface marcadora.

Marck

colored:
Estranho eu tava fazendo um teste aki… e não implementei Cloneable… e mesmo assim funcionou…
POr que Será?

Testou com o Reflection?

colored

Sim Testei. Com Reflection…

Eh q nao tinha percebido meu codigo e achei q estava usando o clone();

agora fica a duvida… pq c for colocar esse metodo em todos os pojos vai ser tenso… pq tem bastante aki no sistema…

agora com reflection ta no eskema já, já ta tratada a exception… so precisar fazer o casting mesmo…
em relação ao uso eu teria que clonar as variaveis de instacia tb… então qto 1 qto o otro daria na mesma…

peczenyj

Acho que esta forma é também interessante.

public class MinhaClasse{
   // outros contrutores
   public MinhaClasse(MinhaClasse c){
     /* entre aqui com a logica de clonagem, tipo
     this.atributo = c.getAtributo(); */
   }
   // o resto da minha classe...
}

MinhaClasse clone = new MinhaClasse(objetoASerClonado);
Marck

colored:
Sim Testei. Com Reflection…

Eh q nao tinha percebido meu codigo e achei q estava usando o clone();

agora fica a duvida… pq c for colocar esse metodo em todos os pojos vai ser tenso… pq tem bastante aki no sistema…

agora com reflection ta no eskema já, já ta tratada a exception… so precisar fazer o casting mesmo…
em relação ao uso eu teria que clonar as variaveis de instacia tb… então qto 1 qto o otro daria na mesma…

Testei aqui com o Reflection e vai clonar todos os objetos internos também.

colored

Bom mas olha soh… marck…
eu fiz o seguinte…

PessoaFisica p = new DAOFactory().getPessoaFisicaDAO().procurarPorId(1);
			PessoaFisica p1 = (PessoaFisica) Clone.clone(p);
			
			System.out.println(p.getEndereco().getNomeEndereco());
			System.out.println(p1.getEndereco().getNomeEndereco());
			
			p1.getEndereco().setNomeEndereco("Julio Colaço");
			
			System.out.println(p1.getEndereco().getNomeEndereco());
			System.out.println(p.getEndereco().getNomeEndereco());

e me retornou…

Santa Rita de Cassia
Santa Rita de Cassia
Julio Colaço
Julio Colaço

Parece q ele tb alterou o original…

Acho q ele Clona o Objeto e so faz referencia ao objeto que eh 1 atributo desse objeto…

Pelo Quando eu pego este objeto do banco ele tem esse problema…
agora c eu crio um objeto na hora ele num da… muito estranho…

colored

peczenyj…

Verdade boa ideia tb…

Marck

Hum… Estranho cara, aqui funcionou:

Pessoa p = new Pessoa("Original", new Carro("Fiesta"));
        Pessoa p1 = (Pessoa) Clone.clone(p);
        p1.n = "Clone";
        p1.carro = new Carro("Ford");
        System.out.println(p.n);
        System.out.println(p.carro.nome);
        System.out.println(p1.n);
        System.out.println(p1.carro.nome);
Original
Fiesta
Clone
Ford

Utilizando aquela classe de reflection, não é?

colored

Sim Com akela mesma kkkk.

peczenyj

Agora eu fiquei pensando qual o motivo para clonar um objeto que justifique usar o método clone ou algo mais elaborado.

Talvez seja o caso cogitar usar objetos imutáveis em alguns momentos. Por exemplo, um Carro, uma vez que eu criei o objeto, não deveria ser possivel alterar os seus atributos a menos que vc gere um novo carro. Dependendo do seu domínio isso pode fazer sentido E clonar este carro ou ter varias variaveis que apontam para a mesma instância não seria um problema devido a imutabilidade.

colored

Vo utilizar isso. para comparar objetos…
pq se por exemplo eu tenho um usuario ja do banco…

usuario = usuarioAuxiliar;

blz… mas eu preciso q esse usuarioAuxiliar mantenha os dados iniciais…
e se eu fizesse isso quando eu alterasse os dados de usuario afetaria tb o usuarioAuxiliar :D…

por isso que preciso fazer.

usuario = usuarioAuxiliar.clone;
peczenyj

Certo mas… não seria o caso de usar o pattern State ou Memento?

colored

Sinceramente, eu procurei mta coisa sobre isso… pq vou precisar utilizar isso varias vezes…
pq eh o seguinte… no sistema aki… o socio do chefe… ele fez a parte de alterações…
e ele faz o seguinte… ele pega um objeto por exemplo com 20 atributos faz session.setAtribute de cada atributo…
e dps pega esse atributo e compra com o objeto no caso faz 20 if’s…
enfim eu terminei toda minha parte. e fui ver a dela e ta esse monte de trankeira e falei pra arrumar isso…
ai eu to procurando soluções mais claras e limpas pra todas as melecas que ele fez entende…

neste caso eu procurei varias coisas me indicaram o Observer mas sei lá n deu mto certo, ai eu achei q esse clone resolveria um pk mais o problema…
mas eu n conheço esses 2 patterns c tem alguma exemplo simples ai???
vo dar uma procurada tb…

tipo vo falar meu problema ai vc por conhecer pod me dizer se rola ou não…

bom eu tenho objeto gravado no banco certinho…
ai tenho uma consulta talz cada linha eu mando por setProperty para o objeto do bean… até ai blz.

agora ele crio uma pagina identica a d cadastro… q so no muda no caso o botao q eh atualizar com metodo atualizar -.-…
qual eh minha ideia utilizar a msm pagina e faço um rendered do botão dependendo d qual ação terá q executar…
pra isso eu preciso quando eu seleciono um objeto na consulta ele executa 1 metodo…

como ele mando o objeto da consulta para um objeto do MB…
eu precisaria de uma copia pra comparar algumas coisas.
mas como disse eu n posso fazer usuario = usuarioAuxiliar
todos nos sabemos pq neh…

esse eh o problema… qual pattern desse vc me indicaria?

dyego.mota
ViniGodoy:
É verdade, vc terá que fazer um try catch.
public Usuario clone(){  
   try { 
       return (Usuario) super.clone();  
   } catch (CloneNotSupported e) {}
}

Ainda é melhor que o throws, pois aí quem usa a classe poderá fazer:

Usuario u2 = u1.clone();

Sem ter que capturar exception alguma.

Muito bom tópico, mas ainda fiquei com uma dúvida: suponhamos uma classe abstrata Pessoa conforme abaixo
public abstract class Pessoa {  
   int matricula;
   String nome;
}

e duas classes Funcionario e Aluno que herdam Pessoa

public class Funcionario extends Pessoa {  
   List<Projeto> projetos;
}

public class Aluno extends Pessoa {  
   List<Disciplina> disciplinas;
}

e que eu tenha um metodo getPessoa que deve retornar Pessoa. Por questões de concistência dos dados eu preciso retornar uma cópia da pessoa, que será Aluno ou Funcionário.
Nesse caso eu terei que implementar o método clone() em cada um dos filhos, pois eles possuem tipos não primitivos e diferentes, certo?
Como minha classe pai é abstrata eu não posso fazer super.clone(); posso?
Assim, vou ter o método clone em Funcionário que irá copiar os dados de Pessoa e a sua lista de projetos e outro método clone em Aluno que também copiará explicitamente os atributos herdados de Pessoa + a sua lista de disciplinas, certo?

bom, como as listas de Funcionario e Aluno não são tipos primitívos nem imutáveis, também terei que implementar clone() na classe Disciplina e Projeto para não passar as referências dos meus objetos reais?

Criado 13 de maio de 2010
Ultima resposta 11 de abr. de 2013
Respostas 29
Participantes 5