Clonagem de objetos em Java usando a interface Cloneable

Veja neste artigo como o mecanismo de clonagem do Java é usado quando se precisa de uma cópia exata de um objeto. Neste artigo, serão explorados os mais importantes aspectos da clonagem em Java.

A interface Cloneable do Java é uma interface que marca, não tem nenhum método. O código a seguir mostra um simples processo de clonagem. A interface Cloneable não tem nenhum membro também, e é usada para indicar que a classe (que implementa a interface) permite uma clonagem bit a bit de um objeto, processo esse simplesmente chamado de "clonagem". Uma exceção conhecida como "CloneNotSupportedException" é mostrada se a função clone() é chamada numa classe em que não se implementou a interface Cloneable. No processo de clonagem o construtor do objeto não é chamado. Então, clonar pode ser definido como uma exata cópia do objeto original.

Listagem 1: Exemplo de classe implementando a interface Cloneable

package com.home.cloning; public class CloneClass implements Cloneable { int a; double b; // This method calls Object's clone(). CloneClass getClone() { try { // call clone in Object. return (CloneClass) super.clone(); } catch (CloneNotSupportedException e) { System.out.println (" Cloning not allowed. " ); return this; } } }

Listagem 2: Exemplo de classe mostrando a clonagem de objetos

package com.home.cloning; public class TestCloneObject { public void testIface() { CloneClass x1 = new CloneClass(); CloneClass x2; x1.a = 15; x1.b = 35.05; x2 = x1.getClone(); // clone x1 System.out.println(" x1: " + x1.a + " " + x1.b); System.out.println(" x2: " + x2.a + " " + x2.b); } public static void main(String args[]) { TestCloneObject testCloneObject = new TestCloneObject(); // test via protected testCloneObject.testIface(); } }

No exemplo acima, o método getClone chama o método clone no objeto e retorna o objeto. É necessário notar-se aqui que o objeto que é retornado depois do mecanismo de clonagem deve ser tipado em seu tipo apropriado, neste caso é uma CloneClass.

Se a classe não está implementando uma interface clonável, e tentarmos clonar este objeto, receberá uma mensagem de CloneNotSupportedException. No processo de clonagem, o construtor não é chamado, ao invés disso, uma cópia exata do dito objeto é criada. Mas o objeto clonado tem que implementar uma interface clonável.

O método clone() da classe Object cria e retorna uma cópia do objeto, com a mesma classe e com os mesmos campos tendo os mesmos valores. No entanto, Object.clone() mostrará uma CloneNotSupportedException a não ser que o objeto seja uma instância de uma classe que implemente o marcador da interface Cloneable.

A implementação padrão do Object.clone() produz uma cópia rasa. Se a classe requerir uma cópia profunda, ou algum outro comportamento customizado, terá de ter seu método clone() sobrescrito também depois de se obter a cópia da superclasse.

Como vantagens da clonagem, tem-se que este mecanismo poupa tempo do desenvolvedor em caso de se precisar criar uma cópia de um objeto. Não seria necessário chamar um novo operador do objeto. Além disso, a clonagem poupa muita tarefas de desenvolvimento, pois um clone é uma cópia exata do objeto.

Como desvantagens da clonagem, tem-se que o tipo de retorno do método de clonagem é um Object. Assim, uma tipagem é requerida no objeto criado. Outra desvantagem, é que não é possível o acesso ao método de clonagem por um tipo abstrato. A maioria das interfaces e classes abstratas em Java não tem como especificar um método de clonagem público. Como resultado, o método de clonagem é somente usado se a verdadeira classe de um objeto é conhecida, o que é contra o princípio da abstração, de usar o tipo mais genérico possível. Por exemplo, se alguém tem uma referência List em Java, esta pessoa não pode invocar o método de clonagem nesta referência por que o List não especifica nenhum método clone() público. Implementações de fato de List como ArrayList e LinkedList todas geralmente têm seus próprios métodos de clonagem, mas isto é inconveniente e uma má abstração para carregar o tipo da classe com um objeto.

Além disso, clonar é uma ação potencialmente perigosa, pois pode ter alguns efeitos colaterais não pretendidos, por exemplo, se o objeto que está sendo clonado contém uma referência a uma variável dizendo refObject, então no objeto clonado, refObject se referirá ao mesmo objeto que o objeto original estava se referindo. Se o clone faz uma mudança no conteúdo do seu refObject, então a mudança será refletida no objeto original também. Considere o exemplo a seguir - se um objeto abre um I/O stream e é clonado, então ambos os objetos serão capazes de operar na mesma stream, então a stream será fechada por ambos e se o segundo objeto tentar escrever para ela, isto causará um erro.

Já que a clonagem pode causar alguns problemas, então o método de clonagem deve ser chamado de dentro de uma classe que está implementando a interface clonável onde o método que está chamando o método de clonagem é protegido, ou ele deve ser explicitamente reescrito pela classe que é pública. No exemplo acima, vimos o processo de clonagem fazendo o clone protegido. O exemplo a seguir ilustra a metodologia da clonagem via overriding:

Listagem 3: Exemplo de classe que implementa a interface Clonable

package com.home.cloning; public class CloneViaOverRiding implements Cloneable { int a; double b; // clone() is now overridden and is public. public Object clone() { try { // call clone in Object. return super.clone(); } catch (CloneNotSupportedException e) { System.out.println("Cloning not allowed."); return this; } } }

Listagem 4: Exemplo de classe que implementa a clonagem usando o método de overriding

package com.home.cloning; public class TestCloneObject { public void testPublic() { CloneViaOverRiding x1 = new CloneViaOverRiding(); CloneViaOverRiding x2; x1.a = 10; x1.b = 20.98; // here, clone() is called directly. x2 = (CloneViaOverRiding) x1. clone (); System.out.println("x1: " + x1.a + " " + x1.b); System.out.println("x2: " + x2.a + " " + x2.b); } public static void main(String args[]) { TestCloneObject testCloneObject = new TestCloneObject(); // test via public testCloneObject.testPublic(); } }

Neste exemplo o método de clonagem da classe do objeto é reescrito e é isto o porquê de ser declarado público em contraste com o exemplo anterior, em que o getClone não tem acesso aos modificadores, fazendo-o acessível somente a nível de pacote.

Em qualquer uma das abordagens, implementar uma interface de clonagem é obrigatório.

Os efeitos colaterais causados pela clonagem são algumas vezes difíceis de identificar no começo. É fácil pensar que uma classe é segura para a clonagem quando na verdade não o é. Em geral, não é recomendado implementar a interface Cloneable para qualquer classe sem ter um sério propósito.

Como alternativas para a clonagem, tem-se algumas alternativas, tais como copiar um construtor, um contrutor-cópia é um construtor que aceita outra instância da mesma classe como parâmetro, um método Factory, esses métodos não são sempre adequados quando o tipo concreto do objeto clonado não é conhecido primeiramente, e o uso a serialização e da desserialização é outra alternativa à clonagem.

Conclusão

Concluímos que a clonagem de objetos é um mecanismo de criar uma cópia de um objeto existente, mas que essa cópia é rasa, que a clonagem poupa trabalho do desenvolvedor, uma vez que a clonagem é feita, o objeto criado é requerido a explicitamente ser tipado no tipo adequado, que a clonagem tem alguns efeitos colaterais, como por exemplo se um objeto que é clonado faz referência a outro objeto, e o novo objeto clonado modifica o objeto que está sendo referenciado, então o objeto original também será mudado. Além de que o mecanismo de clonagem tem algumas alternativas - a cópia de construtor, o método Factory e a serialização e a desserialização.

Por hoje é tudo. Até a próxima!

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados