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!