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
Listagem 2: Exemplo de classe mostrando a clonagem de objetos
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
Listagem 4: Exemplo de classe que implementa a clonagem usando o método de overriding
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.
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.