Por que o método clone () é protegido em java.lang.Object?

112

Qual é o motivo específico clone()definido como protegido em java.lang.Object?

Alex N.
fonte

Respostas:

107

O fato de o clone ser protegido é extremamente duvidoso - assim como o fato de o clonemétodo não ser declarado na Cloneableinterface.

Isso torna o método bastante inútil para fazer cópias de dados porque você não pode dizer :

if(a instanceof Cloneable) {
    copy = ((Cloneable) a).clone();
}

Acho que o design do Cloneableagora é amplamente considerado um erro (citação abaixo). Eu normalmente gostaria de poder fazer implementações de uma interface, Cloneablemas não necessariamente fazer a interfaceCloneable (semelhante ao uso de Serializable). Isso não pode ser feito sem reflexão:

ISomething i = ...
if (i instanceof Cloneable) {
   //DAMN! I Need to know about ISomethingImpl! Unless...
   copy = (ISomething) i.getClass().getMethod("clone").invoke(i);
}

Citação de Josh Bloch's Effective Java :
"A interface clonável foi concebida como uma interface mixin para objetos para anunciar que permitem a clonagem. Infelizmente, ela falha em atender a esse propósito ... Este é um uso altamente atípico de interfaces e não deve ser emulado ... Para que a implementação da interface tenha algum efeito em uma classe, ela e todas as suas superclasses devem obedecer a um protocolo bastante complexo, não executável e amplamente não documentado "

oxbow_lakes
fonte
2
Se o objeto não for clonável, o clone do objeto () lançará CloneNotSupportedException. Portanto, você precisa ser clonável se for chamar super.clone () (resultando na chamada de Object.clone ()). Não vejo como um objeto pode ser serializado sem implementar Serializable.
Steve Kuo
1
"Acho que o design do Cloneable agora é amplamente considerado um erro." [carece de fontes?]
Kevin Panko
Desculpe - eu não estava sugerindo isso. Eu estava apenas sugerindo que "bom" design é não estender uma interface Serializable- cabe às implementações decidir se a implementar Serializable. Eu estava estendendo isso para Cloneable- não é algo que uma interface deva estender - mas a implementação de uma interface é gratuita Cloneable. O problema é que, se você tem um parâmetro do tipo de interface, pergunta se ele é clonável; mas não é possível cloná-lo!
oxbow_lakes
6
@Kevin - Java pp45 efetivo de Josh Bloch . "A interface Cloneable foi concebida como uma interface mixin para objetos anunciarem que eles permitem a clonagem. Infelizmente, ela não serve a esse propósito"
oxbow_lakes
Também na mesma página: "Este é um uso altamente atípico de interfaces e não deve ser emulado" e "Para que a implementação da interface tenha algum efeito em uma classe, ela e todas as suas superclasses devem obedecer a um padrão bastante complexo, protocolo não executável e em grande parte não documentado "
oxbow_lakes
30

A interface Clonable é apenas um marcador dizendo que a classe pode suportar clone. O método é protegido porque você não deve chamá-lo no objeto, você pode (e deve) sobrescrevê-lo como público.

Da Sun:

Na classe Object, o método clone () é declarado protegido. Se tudo o que você fizer for implementar Cloneable, apenas as subclasses e membros do mesmo pacote serão capazes de invocar clone () no objeto. Para permitir que qualquer classe em qualquer pacote acesse o método clone (), você terá que sobrescrevê-lo e declará-lo público, como é feito a seguir. (Ao substituir um método, você pode torná-lo menos privado, mas não mais privado. Aqui, o método clone () protegido em Object está sendo substituído como um método público.)

Bill K
fonte
O que é bom, até que vocêSet
inclua as
@oxbow_lakes: mas talvez algumas implementações de Set não sejam clonáveis
newacct
3
Você não pode clonar nada que não implemente a interface Clonable - é um marcador que diz "Esta classe é clonável corretamente" - muito semelhante à interface Serializable. A propósito, existe uma maneira de clonar classes via serialização que funciona bem - google algo como "clone de serialização java" e você provavelmente encontrará um punhado de maneiras de obter uma cópia profunda do seu objeto.
Bill K de
4
Você não pode clonar nada que não implemente a interface clonável, mas só porque algo implementa a interface clonável não significa que você pode cloná-lo.
Michael Myers
1
@BuckCherry toString tem uma implementação padrão, se você chamá-la, algo bom acontecerá e você receberá de volta uma string. igual a tem uma implementação padrão (igual a ==). O clone não pode ter uma implementação padrão. Se você chamar clone em um objeto que não o implementou, não obterá um comportamento razoável. A clonagem é complicada e não pode ser feita automaticamente (alguns objetos sem construtores padrão podem ser impossíveis de clonar genericamente), então, por padrão, eles a tornam um pouco mais segura. Acho que colocá-lo em Object, entretanto, pode não ter sido necessário.
Bill K de
7

cloneestá protegido porque é algo que deve ser sobrescrito para que seja específico para a classe atual. Embora fosse possível criar um clonemétodo público que clonaria qualquer objeto, isso não seria tão bom quanto um método escrito especificamente para a classe que precisa dele.

Andrew Hare
fonte
mas por que tem que ser protegido para isso?
Janusz
3
É protegido para que você não use aquele no objeto (ele apenas lançará uma exceção de qualquer maneira). Eles querem que você o substitua em uma aula, então você o torna público. (respondido algumas vezes abaixo também)
Bill K
1
E inútil ao considerar interfaces de acordo com meu ponto abaixo
oxbow_lakes
deve ser substituído não é muito claro, então deveria ser abstrato e não deve ser na classe de objeto. Estou tentando entender por que é assim.
Kumar Abhishek
4

O método Clone não pode ser usado diretamente em nenhum objeto, e é por isso que ele deve ser sobrescrito pela subclasse.

Claro que poderia ser público e apenas lançar uma exceção apropriada quando a clonagem não for possível, mas acho que seria enganoso.

A maneira como o clone está implementado agora faz você pensar sobre por que deseja usar o clone e como deseja que seu objeto seja clonado.

Silfverstrom
fonte
2

Ele é protegido porque a implementação padrão faz uma cópia superficial de todos os campos (incluindo privado), contornando o construtor . Isso não é algo que um objeto possa ser projetado para tratar em primeiro lugar (por exemplo, ele pode controlar as instâncias do objeto criado em uma lista compartilhada ou algo semelhante).

Pelo mesmo motivo, a implementação padrão de clone()lançará se o objeto no qual é chamado não implementar Cloneable. É uma operação potencialmente insegura com consequências de longo alcance e, portanto, o autor da classe deve aceitar explicitamente.

Pavel Minaev
fonte
Na verdade, a implementação padrão (no objeto) lança uma exceção de acordo com os documentos ...
Bill K
2
Não, não apenas joga. De seu JavaDoc: "O clone do método para a classe Object executa uma operação de clonagem específica. Primeiro, se a classe deste objeto não implementa a interface Cloneable, então uma CloneNotSupportedException é lançada. Observe que todos os arrays são considerados para implementar a interface Cloneable. Caso contrário, este método cria uma nova instância da classe deste objeto e inicializa todos os seus campos com exatamente o conteúdo dos campos correspondentes deste objeto, como se por atribuição; os conteúdos dos campos não são eles próprios clonados. "
Pavel Minaev
2

Do javadoc de clonável.

* By convention, classes that implement this interface (cloneable) should override 
* <tt>Object.clone</tt> (which is protected) with a public method.
* See {@link java.lang.Object#clone()} for details on overriding this
* method.

* Note that this interface does <i>not</i> contain the <tt>clone</tt> method.
* Therefore, it is not possible to clone an object merely by virtue of the
* fact that it implements this interface.  Even if the clone method is invoked
* reflectively, there is no guarantee that it will succeed.

Portanto, você poderia chamar clone em cada objeto, mas isso não lhe daria na maioria das vezes os resultados desejados ou uma exceção. Mas só é encorajado se você implementar clonável.

Janusz
fonte
2
Você não pode chamar clone em todos os objetos, porque eles estão protegidos!
Pavel Minaev
2

IMHO é tão simples quanto isto:

  • #clone não deve ser chamado em objetos não clonáveis, portanto, não é tornado público
  • #clonedeve ser chamado por subclasses ob Objectque implementam Cloneable para obter a cópia superficial da classe certa

Qual é o escopo correto para métodos que devem ser chamados por subclasses, mas não por outras classes?

É protected.

A implementação Cloneablede classes, é claro, tornará esse método público para que ele possa ser chamado de outras classes.

Michaela Maura Elschner
fonte
0

O método Clone () tem uma verificação interna de 'instância de Cloneable ou não'. É assim que a equipe Java pode pensar que restringirá o uso impróprio do método clone (). O método clone () é protegido, ou seja, acessado apenas por subclasses. Uma vez que object é a classe pai de todas as subclasses, o método Clone () pode ser usado por todas as classes de fato se não tivermos a verificação acima de 'instância de Cloneable'. Esta é a razão pela qual a equipe Java pode ter pensado em restringir o uso impróprio de clone (), tendo a verificação no método clone () 'é uma instância de Cloneable'.

Portanto, qualquer classe que implemente clonável pode usar o método clone () da classe Object.

Além disso, por ser protegido, está disponível apenas para as subclasses que implementam interface clonável. Se quisermos torná-lo público, esse método deve ser substituído pela subclasse com sua própria implementação.

SARIKA
fonte
-2

Sim, mesmo problema que conheci. Mas eu resolvo isso implementando este código

public class Side implements Cloneable {
    public Side clone() {

        Side side = null;
        try {
            side = (Side) super.clone();
        } catch (CloneNotSupportedException e) {
            System.err.println(e);
        }
        return side;
    }
}

Exatamente como alguém disse antes.

Fyhao
fonte
1
CloneNotSupportedException é outro exemplo de uma exceção verificada que deve ser desmarcada (ou seja, deve estender RuntimeException, não Exception). Embora o método clone () na classe Side implemente Cloneable e, portanto, nunca lance CloneNotSupportedException, Side.clone () ainda deve capturar ou declarar a exceção. Isso apenas adiciona ruído de manipulação de exceção supérfluo ao método clone ().
Derek Mahar
-2

Bem, também os desenvolvedores do sol são apenas humanos e, de fato, cometeram um grande erro ao implementar o método clone como protegido, o mesmo erro que implementaram um método clone não funcional em ArrayList! Portanto, em geral, existe um mal-entendido muito mais profundo, mesmo de programadores Java experientes, sobre o método clone.

No entanto, recentemente encontrei uma solução rápida e fácil para copiar qualquer objeto com todo o seu conteúdo, não importa como seja construído e o que contenha, veja minha resposta aqui: Bug no uso de Object.clone ()

marca
fonte
-3

Mais uma vez, a estrutura Java JDK mostra um pensamento brilhante:

A interface clonável não contém um "clone T público ();" método porque atua mais como um atributo (por exemplo, Serializable) que permite que uma instância seja clonada.

Não há nada de errado com este design porque:

  1. Object.clone () não fará o que você deseja com sua classe personalizada.

  2. Se você tem Myclass implementa Cloneable => você sobrescreve clone () com "public MyClass clone ()"

  3. Se você tiver MyInterface extends Cloneable e algumas MyClasses implementando MyInterface: simplesmente defina "public MyInterface clone ();" na interface e todos os métodos que usam objetos MyInterface serão capazes de cloná-los, não importando sua classe MyClass.

Vencedor
fonte
2
se sua classe for herdada, sua implementação de clone de classe derivada não será segura até que suas implementações de classe base forneçam métodos de clone seguros. Além disso, é uma condição de design incomum ter uma interface sem método / atributo. Essa interface não força a classe a implementar o clone.
prap19