Java: solução recomendada para clonagem profunda / cópia de uma instância

Eu estou querendo saber se existe uma maneira recomendada de fazer clone / cópia profunda da instância em java.

Tenho 3 soluções em mente, mas posso ter saudades, e gostaria de ter sua opinião

editar: include Bohzo propositon e refinar a questão: trata-se mais de clonagem profunda do que de clonagem superficial.

Faça Você Mesmo:

codifique o clone manualmente depois das propriedades e verifique se as instâncias mutáveis ​​também são clonadas.
pró:
– controle do que será realizado
– execução rápida
contras:
– tedioso para escrever e manter
– propenso a erros (falha de copiar / colar, propriedade ausente, propriedade mutável reatribuída)

Use reflection:

Com suas próprias ferramentas de reflection ou com um auxiliar externo (como o jakarta common-beans), é fácil escrever um método de cópia genérico que fará o trabalho em uma linha.
pró:
– fácil de escrever
– sem manutenção
contras:
– menos controle do que acontece
– propenso a bugs com object mutável se a ferramenta de reflection não clonar sub objects também
– execução mais lenta

Use o quadro clone:

Use um framework que faça isso para você, como:
commons-lang SerializationUtils
Biblioteca de Clonagem Profunda Java
Trator
Kryo

pró:
– o mesmo que a reflection
– mais controle sobre o que será exatamente clonado.
contras:
– cada instância mutável é totalmente clonada, mesmo no final da hierarquia
– pode ser muito lento para executar

Use instrumentação de bytecode para escrever clone em tempo de execução

javassit , BCEL ou cglib podem ser usados ​​para gerar um cloner dedicado tão rápido quanto uma mão. Alguém conhece um lib usando uma dessas ferramentas para esse fim?

O que eu perdi aqui?
Qual desses você recomendaria ?

Obrigado.

Para clonagem profunda (clona toda a hierarquia de objects):

  • commons-lang SerializationUtils – usando serialização – se todas as classs estiverem sob seu controle e você puder forçar a implementação Serializable .

  • Java Deep Cloning Library – usando reflection – nos casos em que as classs ou os objects que você deseja clonar estão fora de seu controle (uma biblioteca de terceiros) e você não pode fazê-los implementar Serializable , ou em casos que você não deseja implementar Serializable .

Para clonagem superficial (clona apenas as propriedades de primeiro nível):

  • commons-beanutils BeanUtils – na maioria dos casos.

  • Spring BeanUtils – se você já estiver usando spring e, portanto, tiver este utilitário no classpath.

Eu deliberadamente omiti a opção “faça você mesmo” – as APIs acima fornecem um bom controle sobre o que e o que não clonar (por exemplo, usando transient , ou String[] ignoreProperties ), então reinventar a roda não é o preferido.

O livro de Joshua Bloch tem um capítulo inteiro intitulado “Item 10: Override Clone Judiciously”, no qual ele explica por que a substituição do clone em sua maior parte é uma má ideia, porque a especificação do Java cria muitos problemas.

Ele fornece algumas alternativas:

  • Use um padrão de fábrica no lugar de um construtor:

      public static Yum newInstance(Yum yum); 
  • Use um construtor de cópia:

      public Yum(Yum yum); 

Todas as classs de coleção em Java suportam o construtor de cópia (por exemplo, novo ArrayList (l);)

Desde a versão 2.07, o Kryo suporta clonagem superficial / profunda :

 Kryo kryo = new Kryo(); SomeClass someObject = ... SomeClass copy1 = kryo.copy(someObject); SomeClass copy2 = kryo.copyShallow(someObject); 

Kryo é rápido, no e de sua página você pode encontrar uma lista de empresas que o utilizam na produção.

Use XStream toXML / fromXML na memory. Extremamente rápido e tem sido em torno de um longo tempo e está indo forte. Objetos não precisam ser Serializable e você não tem reflection de uso (embora XStream faça). XStream pode discernir variables ​​que apontam para o mesmo object e não fazer duas cópias completas da instância. Muitos detalhes como esse foram elaborados ao longo dos anos. Eu usei isso por vários anos e é um ir para. É tão fácil de usar quanto você pode imaginar.

 new XStream().toXML(myObj) 

ou

 new XStream().fromXML(myXML) 

Clonar,

 new XStream().fromXML(new XStream().toXML(myObj)) 

Mais sucintamente:

 XStream x = new XStream(); Object myClone = x.fromXML(x.toXML(myObj)); 

Depende

Para velocidade, use DIY. Para a prova de balas, use a reflection.

BTW, a serialização não é o mesmo que refl, já que alguns objects podem fornecer methods de serialização substituídos (readObject / writeObject) e eles podem estar com bugs

Eu recomendo a maneira DIY que, combinada com um bom método hashCode () e equals () deve ser fácil de provar em um teste de unidade.

Eu sugiro replace Object.clone (), chame super.clone () primeiro e depois chame ref = ref.clone () em todas as referências que você deseja que sejam copiadas em profundidade. É mais ou menos Faça você mesmo , mas precisa de um pouco menos de codificação.

Para objects complicados e quando o desempenho não é significativo, eu uso o gson para serializar o object para o texto json e, em seguida, desserializo o texto para obter um novo object.

gson que com base na reflection funcionará na maioria dos casos, exceto que transient campos transient não serão copiados e os objects com referência circular com causa StackOverflowError .

 public static  ObjectType Copy(ObjectType AnObject, Class ClassInfo) { Gson gson = new GsonBuilder().create(); String text = gson.toJson(AnObject); ObjectType newObject = gson.fromJson(text, ClassInfo); return newObject; } public static void main(String[] args) { MyObject anObject ... MyObject copyObject = Copy(o, MyObject.class); }