Preciso implementar um clone profundo em um dos meus objetos que não tem superclasse.
Qual é a melhor maneira de lidar com o check CloneNotSupportedException
lançado pela superclasse (que é Object
)?
Um colega de trabalho me aconselhou a lidar com isso da seguinte maneira:
@Override
public MyObject clone()
{
MyObject foo;
try
{
foo = (MyObject) super.clone();
}
catch (CloneNotSupportedException e)
{
throw new Error();
}
// Deep clone member fields here
return foo;
}
Esta parece ser uma boa solução para mim, mas eu gostaria de jogá-la na comunidade StackOverflow para ver se há outras informações que posso incluir. Obrigado!
Cloneable
, lançar um emAssertionError
vez de apenas um planoError
é um pouco mais expressivo.Respostas:
Você absolutamente tem que usar
clone
? A maioria das pessoas concorda que o Javaclone
está quebrado.Josh Bloch no projeto - Construtor de cópia versus clonagem
Você pode ler mais discussões sobre o tópico em seu livro Effective Java 2nd Edition, Item 11: Override
clone
judiciosamente . Ele recomenda, em vez disso, usar um construtor de cópias ou uma fábrica de cópias.Ele escreveu páginas de páginas sobre como, se você sentir que deve, você deve implementar
clone
. Mas ele fechou com isso:A ênfase era dele, não minha.
Como você deixou claro que não tem escolha a não ser implementar
clone
, aqui está o que você pode fazer neste caso: certifique-se dissoMyObject extends java.lang.Object implements java.lang.Cloneable
. Se for esse o caso, você pode garantir que NUNCA pegará aCloneNotSupportedException
. LançarAssertionError
como alguns sugeriram parece razoável, mas você também pode adicionar um comentário que explica por que o bloco catch nunca será inserido neste caso específico .Como alternativa, como outros também sugeriram, talvez você possa implementar
clone
sem chamarsuper.clone
.fonte
super.clone()
dentro de seus métodos de clone, uma subclasse geralmente só terá que substituirclone()
se adicionar novos campos cujo conteúdo precisaria ser clonado. Se alguma superclasse usar emnew
vez desuper.clone()
, então todas as subclasses devem sobrescreverclone()
se adicionam ou não novos campos.Às vezes, é mais simples implementar um construtor de cópia:
Economiza o trabalho de manuseio
CloneNotSupportedException
, trabalha comfinal
campos e você não precisa se preocupar com o tipo a retornar.fonte
A maneira como seu código funciona é muito parecida com a maneira "canônica" de escrevê-lo. Eu jogaria um
AssertionError
dentro da captura, no entanto. Sinaliza que essa linha nunca deve ser alcançada.fonte
Cloneable
em geral, é uma ideia quebrada, conforme explicado em Effective Java. O OP já expressou que eles têm que usarCloneable
. Portanto, não tenho ideia de como minha resposta poderia ser melhorada, exceto talvez excluí-la de uma vez.Existem dois casos em que o
CloneNotSupportedException
será lançado:Cloneable
(assumindo que a clonagem real eventualmente adie paraObject
o método de clone de). Se a classe que você está escrevendo este método em implementaCloneable
, isso nunca acontecerá (já que qualquer subclasse irá herdá-lo apropriadamente).Cloneable
.O último caso não pode ocorrer em sua classe (já que você está chamando diretamente o método da superclasse no
try
bloco, mesmo se invocado a partir de uma chamada de subclassesuper.clone()
) e o primeiro não deve, já que sua classe claramente deve implementarCloneable
.Basicamente, você deve registrar o erro com certeza, mas neste caso em particular isso só acontecerá se você bagunçar a definição da sua classe. Portanto, trate-o como uma versão verificada de
NullPointerException
(ou semelhante) - ele nunca será lançado se seu código for funcional.Em outras situações, você precisa estar preparado para esta eventualidade - não há garantia de que um determinado objeto seja clonável, portanto, ao capturar a exceção, você deve tomar as medidas adequadas dependendo desta condição (continue com o objeto existente, execute uma estratégia de clonagem alternativa por exemplo, serializar-desserializar, lançar um
IllegalParameterException
se seu método requer o parâmetro por clonável, etc. etc.).Edit : Embora no geral eu deva apontar que sim,
clone()
realmente é difícil implementar corretamente e para os chamadores saber se o valor de retorno será o que eles desejam, duplamente quando você considera clones profundos versus clones rasos. Muitas vezes, é melhor apenas evitar a coisa toda e usar outro mecanismo.fonte
super.clone()
.Use a serialização para fazer cópias profundas. Esta não é a solução mais rápida, mas não depende do tipo.
fonte
Você pode implementar construtores de cópia protegida como:
fonte
clone()
o objeto retornado por, agetMySecondMember()
menos que tenha umpublic clone
método.Por mais que a maioria das respostas aqui sejam válidas, preciso dizer que sua solução também é como os desenvolvedores de API Java reais fazem. (Josh Bloch ou Neal Gafter)
Aqui está um extrato de openJDK, classe ArrayList:
Como você notou e outros mencionaram,
CloneNotSupportedException
quase não tem chance de cair se você declarar que implementou aCloneable
interface.Além disso, não há necessidade de substituir o método se você não fizer nada de novo no método substituído. Você só precisa substituí-lo quando precisar fazer operações extras no objeto ou torná-lo público.
Em última análise, ainda é melhor evitá-lo e fazê-lo de outra forma.
fonte
fonte
Só porque a implementação do Java de Cloneable está quebrada, não significa que você não pode criar um por conta própria.
Se o propósito real do OP era criar um clone profundo, acho que é possível criar uma interface como esta:
em seguida, use o construtor de protótipo mencionado antes para implementá-lo:
e outra classe com um campo de objeto AClass:
Desta forma, você pode facilmente clonar profundamente um objeto da classe BClass sem a necessidade de @SuppressWarnings ou outro código enganoso.
fonte