Por que o Cloneable não é preterido?

139

É comumente entendido que a Cloneableinterface em Java está quebrada. Há muitas razões para isso, que não mencionarei; outros já fizeram isso. É também a posição dos próprios arquitetos Java .

Minha pergunta é, portanto: por que ainda não foi preterido? Se a equipe principal do Java decidiu que está quebrada, também deve ter considerado a descontinuação. Quais são os motivos deles para fazê-lo (no Java 8 ainda não foi preterido )?

Kao
fonte
41
Esta questão não é "primariamente baseada em opiniões", como muitos aparentemente se sentem autorizados a julgar. Aqueles que têm apenas uma opinião sobre os motivos simplesmente não estão qualificados para responder. No entanto, é verdade que você tem apenas uma chance remota de obter uma resposta autorizada aqui. Também é verdade que sua pergunta não é sobre um problema solucionável que você tem, por isso é pelo menos fora de tópico.
Marko Topolnik # 16/14
6
@MarkoTopolnik Concordo que existem algumas pessoas no mundo que poderiam fornecer uma resposta autorizada, mas não acredito que esse seja o teste que aplicamos aqui. O motivo do fechamento afirma que "as respostas a essa pergunta tendem a ser quase inteiramente baseadas em opiniões". Eu suspeito que será o caso aqui, a menos que tenhamos muita sorte.
Duncan Jones
2
Aqui está "Como e Quando" para descontinuar o uso do Oracle ... ( docs.oracle.com/javase/6/docs/technotes/guides/javadoc/… ) A interface clonável pode cair no caso "com erros ou altamente ineficiente", mas é muito aberto a opiniões.
Maxx
8
@Duncan Ainda não considero justo julgar a questão com base em minhas suposições sobre a falta de disciplina por parte dos respondentes . Se um usuário não souber o motivo do (s) questionamento (s), ele não poderá abusar do serviço de atendimento ao cliente para apresentar sua opinião sobre o assunto.
Marko Topolnik
4
@lexicore Sim, exatamente --- e você pode apostar que eles consideraram completamente essa opção e, por implicação, devem ter fortes razões para não depreciá-la. Suas próprias críticas Cloneablesão amplamente conhecidas.
Marko Topolnik 16/10

Respostas:

120

Há um bug enviado em 1997 ao Java Bug Database sobre a adição de clone()método Cloneable, portanto ele não seria mais inútil. Foi fechado com a resolução "não corrigido" e a justificativa é a seguinte:

O Comitê de Revisão Técnica (TRC) da Sun considerou esse problema detalhadamente e recomendou que não fosse tomada nenhuma ação além de melhorar a documentação da atual interface Cloneable . Aqui está o texto completo da recomendação:

As APIs de clonagem de objetos Java existentes são problemáticas. Existe um método "clone" protegido em java.lang.Object e existe uma interface java.lang.Cloneable. A intenção é que, se uma classe deseja permitir que outras pessoas a clonem, ela deve oferecer suporte à interface Cloneable e substituir o método de clone protegido padrão por um método de clone público. Infelizmente, por razões convenientemente perdidas nas brumas do tempo, a interface Cloneable não define um método de clone.

Essa combinação resulta em uma quantidade razoável de confusão. Algumas classes afirmam dar suporte ao Cloneable, mas acidentalmente esquecem de dar suporte ao método clone. Os desenvolvedores estão confusos sobre como o Cloneable deve funcionar e o que o clone deve fazer.

Infelizmente, adicionar um método "clone" ao Cloneable seria uma alteração incompatível. Não quebrará a compatibilidade binária, mas quebrará a compatibilidade da fonte. Evidências anedóticas sugerem que, na prática, existem vários casos em que as classes suportam a interface Cloneable, mas não fornecem um método de clone público. Após discussão, a TRC recomendou por unanimidade que NÃO modificássemos a interface Cloneable existente, devido ao impacto na compatibilidade.

Uma proposta alternativa era adicionar uma nova interface java.lang.PubliclyCloneable para refletir o objetivo original do Cloneable. Por uma maioria de 5 a 2, a TRC recomendou isso. A principal preocupação era que isso acrescentaria ainda mais confusão (incluindo confusão de ortografia!) A uma imagem já confusa.

A TRC recomendou por unanimidade que adicionássemos documentação adicional à interface Cloneable existente para melhor descrever como ela deve ser usada e para descrever as "melhores práticas" para implementadores.

Portanto, embora isso não seja diretamente preterido , o motivo para não tornar o Cloneable "preterido" é que o Comitê de Revisão Técnica decidiu que modificar a documentação existente será suficiente para tornar essa interface útil. E assim eles fizeram. Até o Java 1.4, Cloneablefoi documentado da seguinte maneira:

Uma classe implementa a interface Cloneable para indicar ao método Object.clone () que é legal para esse método fazer uma cópia de campo a campo das instâncias dessa classe.

Tentativas de clonar instâncias que não implementam a interface Cloneable resultam na exceção CloneNotSupportedException sendo lançada.

A interface Cloneable não declara métodos.

Desde o Java 1.4 (lançado em fevereiro de 2002) até a edição atual (Java 8), fica assim:

Uma classe implementa a interface Cloneable para indicar ao método Object.clone () que é legal para esse método fazer uma cópia de campo a campo das instâncias dessa classe. A chamada do método clone do Object em uma instância que não implementa a interface Cloneable resulta na exceção CloneNotSupportedException sendo lançada.

Por convenção, as classes que implementam essa interface devem substituir Object.clone (que está protegido) por um método público. Consulte Object.clone () para obter detalhes sobre como substituir esse método.

Observe que essa interface não contém o método clone. Portanto, não é possível clonar um objeto apenas pelo fato de ele implementar essa interface. Mesmo que o método clone seja invocado de forma reflexiva, não há garantia de que será bem-sucedido.

Kao
fonte
3
e você sabe por que o clonemétodo estava em Objectprimeiro lugar?
Njzk2 16/10
8
@ njzk2 Essa é uma parte essencial do mecanismo - é o método que faz a mágica extralinguística de baixo nível de copiar a imagem do objeto pouco a pouco.
Marko Topolnik 16/10
3
@ Unheilig Essa mágica é algo que você não pode replicar com um construtor de cópias: Object#clone()produz uma instância da mesma classe que a original, sem que essa classe seja conhecida no momento da compilação.
Marko Topolnik
4
@AVolpe: Isso não resolveria a incompatibilidade de origem mencionada na resposta "onde as classes suportam a interface Cloneable, mas falham em fornecer um método de clone público". Em particular, as classes que fornecem um método de clone não público seriam quebradas.
Louis Wasserman
2
Boa história. Aqui está um link direto para o banco de dados de bugs. Adicionei mais um pouco de história e citei partes dele na minha resposta .
Stuart Marks
64

A resposta curta para "por que não está Cloneableobsoleta?" (ou, de fato, por que não é Xpreterido, para qualquer um X) é que não houve muita atenção para preteri-los.

A maioria das coisas que foram descontinuadas recentemente foram descontinuadas porque existe um plano específico para removê-las. Por exemplo, os métodos addPropertyChangeListenere removePropertyChangeListenerdo LogManager foram descontinuados no Java SE 8 com a intenção de removê-los no Java SE 9. (O motivo é que eles complicam desnecessariamente as interdependências de módulos.) De fato, essas APIs já foram removidas do desenvolvimento inicial do JDK 9 constrói. (Observe que chamadas de ouvinte de alteração de propriedade semelhantes também foram removidas Pack200; consulte JDK-8029806 .)

Não existe um plano semelhante para Cloneablee para Object.clone().

Uma resposta mais longa envolveria a discussão de perguntas adicionais, como o que se poderia esperar dessas APIs, quais custos ou benefícios acumulariam a plataforma se fossem preteridos e o que está sendo comunicado aos desenvolvedores quando uma API é preterida. Eu explorei esse tópico em minha recente palestra sobre JavaOne, Dívida e Deprecação . (Slides disponíveis nesse link; vídeo aqui .) Acontece que o JDK em si não tem sido muito consistente em seu uso de depreciação. Tem sido usado para significar várias coisas diferentes, incluindo, por exemplo,

  • Isso é perigoso e você deve estar ciente dos riscos de usá-lo (exemplo: Thread.stop(), Thread.resume(), e Thread.suspend()).

  • Isso será removido em uma versão futura

  • Isso é obsoleto e é uma boa ideia usar algo diferente (exemplo: muitos dos métodos em java.util.Date)

Todos esses são significados distintos, e diferentes subconjuntos deles se aplicam a coisas diferentes que foram preteridas. E alguns subconjuntos aplicam-se a coisas que não são preteridas (mas que talvez devam ser preteridas).

Cloneablee Object.clone()estão "quebrados" no sentido de que possuem falhas de design e são difíceis de usar corretamente. No entanto, clone()ainda é a melhor maneira de copiar matrizes e a clonagem tem alguma utilidade limitada para fazer cópias de instâncias de classes cuidadosamente implementadas. Remover a clonagem seria uma alteração incompatível que quebraria muitas coisas. Uma operação de clonagem poderia ser reimplementada de uma maneira diferente, mas provavelmente seria mais lenta que Object.clone().

No entanto, para a maioria das coisas, um construtor de cópias é preferível à clonagem. Então, talvez marcar Cloneablecomo "obsoleto" ou "substituído" ou algo semelhante seja apropriado. Isso indicaria aos desenvolvedores que eles provavelmente querem procurar em outro lugar, mas não indicaria que o mecanismo de clonagem possa ser removido em uma versão futura. Infelizmente, esse marcador não existe.

No momento, "depreciação" parece implicar uma remoção eventual - apesar do fato de um número extremamente pequeno de recursos obsoletos ter sido removido - e, portanto, a depreciação não parece justificada para o mecanismo de clonagem. Talvez no futuro possa ser aplicada uma marcação alternativa que instrua os desenvolvedores a usar mecanismos alternativos.

ATUALIZAR

Adicionei um histórico adicional ao relatório de erros . Frank Yellin, um dos primeiros implementadores da JVM e co-autor da especificação da JVM, fez alguns comentários em resposta ao comentário "perdido nas brumas do tempo" na recomendação TRC citada na outra resposta . Eu citei as partes relevantes aqui; a mensagem completa está no relatório de erros.

Cloneable não tem métodos pela mesma razão que Serializable não. Cloneable indica uma propriedade da classe, em vez de dizer especificamente algo sobre os métodos que a classe suportava.

Antes da reflexão, precisávamos de um método nativo para fazer uma cópia superficial de um Objeto. Assim, Object.clone () nasceu. Também ficou claro que muitas classes gostariam de substituir esse método e que nem todas as classes gostariam de ser clonadas. Daí Cloneable nasceu para indicar a intenção do programador.

Então, em suma. O objetivo do Cloneable não era indicar que você tinha um método público clone (). Era para indicar que você estava disposto a ser clonado usando Object.clone (), e cabia à implementação decidir se tornaria público ou não o clone ().

Stuart Marks
fonte
3
Uma boa resposta que você tem aqui, senhor. Eu gosto especialmente que você não joga Object.clone()no fogo só porque todo mundo quer, mas você está disposto a raciocinar e trazer à tona as coisas boas dele.
icza 17/10
2
No entanto, clone () ainda é a melhor maneira de copiar matrizes, e a clonagem tem alguma utilidade limitada para fazer cópias de instâncias de classes que são cuidadosamente implementadas. Fiquei impressionado com a correção de 6428387, todos os caminhos de código (clone, novo / arrayCopy, Arrays.copyOf) resultaram nos mesmos intrínsecos. Alguma coisa mudou recentemente?
21714
2
@ bestsss Eu não acho que array.clone()é necessariamente mais rápido do que quaisquer alternativas. Da perspectiva da API, é a maneira mais concisa de duplicar uma matriz. Arrays.copyOf(array, newlen)chega perto, mas requer um parâmetro de comprimento, que é redundante se você não estiver alterando o comprimento.
Stuart Marks
2
@ Holger Sim, até onde podemos ver, esta é a primeira remoção real de uma API desde a 1.1. Observe também que, embora concordemos que Thread.suspend()e Thread.stop()(no-arg) sejam perigosos, eles provavelmente não serão removidos - ou alterados para gerar uma exceção incondicionalmente - porque as pessoas realmente os usam! Presumivelmente, eles estão dispostos a assumir o risco. Um dos fatores atenuantes dos ouvintes de mudança de propriedade é que eles foram usados ​​muito raramente, portanto o impacto de removê-los é pequeno.
Stuart Marks
2
@Holger Conceitualmente, java.beanspode ser feito independente, java.desktoppois os beans são apenas uma API de biblioteca para propriedades. Infelizmente, se você pesquisar na API de beans, há muitas dependências no AWT. A implementação tem ainda mais. Certamente, pode ser possível extraí-los, mas fazer isso parece muito mais trabalhoso do que, digamos, desagrupar o registro de grãos. Todo o esforço de modularização é sobre fazer esse desembaraço; sem dúvida, mais poderia ser feito, mas o Jigsaw levaria ainda mais tempo.
Stuart Marks
-1

por que ainda não foi preterido?

Porque o JCP não achou oportuno fazê-lo, e talvez nunca o faça. Pergunte a eles. Você está perguntando no lugar errado.

Quais são as razões por trás de manter essa coisa na API Java

Ninguém jamais removerá nada da API Java, devido ao requisito de compatibilidade com versões anteriores. A última vez que aconteceu foi a alteração no modelo de evento AWT entre 1,0 e 1,1 em 1996/7.

Marquês de Lorne
fonte
17
Eles foram removidos (efetivamente) Thread.stop(Throwable)alterando seu contrato para sempre serem lançados UnsupportedOperationExceptionno chamador (não no segmento de destino!).
Marko Topolnik 16/10
22
O que aconteceu na mesma época? A remoção da Thread.stop(Throwable)funcionalidade do aconteceu em Java 8. De qualquer forma, o conselho não qualificado de "pedir a eles" está errado, porque hoje o próprio arquiteto-chefe Java é um membro ativo do Stack Overflow. Ele simplesmente não se incomoda em responder nada além de perguntas relacionadas ao Streams.
Marko Topolnik 16/10
13
Além disso, a pergunta do OP não é sobre remoção , mas sobre depreciação , e claramente a depreciação tem acontecido o tempo todo.
Marko Topolnik
17
@EJP Não estou perguntando se Cloneableserá removido da API Java. Estou perguntando por que não será depreciado. E acho que este é o lugar perfeito para perguntar.
Kao
5
@VaheHarutyunyan Obrigado pela mensagem, mas não sou arquiteto Java. Sou engenheiro do grupo JDK da Oracle, que mantém essas coisas.
Stuart Marcas