Java: soluzione consigliata per la clonazione / copia profonda di un’istanza

Mi chiedo se c’è un modo consigliato di fare clone / copia dell’istanza in java.

Ho in mente 3 soluzioni, ma posso averne la mancanza, e mi piacerebbe avere la tua opinione

modifica: includi il propositon di Bohzo e affina la domanda: si tratta più della clonazione profonda che della clonazione superficiale.

Fallo da solo:

codifica le proprietà del clone manualmente dopo le proprietà e verifica che anche le istanze mutabili siano clonate.
pro:
– controllo di ciò che verrà eseguito
– Esecuzione rapida
contro:
– noioso scrivere e mantenere
– bug incline (errore di copia / incolla, proprietà mancante, proprietà mutable riassegnata)

Usa la riflessione:

Con i tuoi strumenti di riflessione o con un helper esterno (come i fagioli comuni di jakarta) è facile scrivere un metodo di copia generico che svolgerà il lavoro in un’unica riga.
pro:
– facile da scrivere
– nessuna manutenzione
contro:
– meno controllo di ciò che accade
– bug incline con object mutevole se lo strumento di riflessione non clona troppo gli oggetti secondari
– esecuzione più lenta

Usa il framework clone:

Usa un framework che lo faccia per te, come:
Commons-lang SerializationUtils
Java Deep Cloning Library
Dozer
Kryo

pro:
– Come la riflessione
– più controllo su ciò che sarà esattamente clonato.
contro:
– ogni istanza mutabile è completamente clonata, anche alla fine della gerarchia
– Potrebbe essere molto lento da eseguire

Utilizzare la strumentazione bytecode per scrivere clone in fase di esecuzione

javassit , BCEL o cglib potrebbero essere utilizzati per generare un cloner dedicato con la stessa rapidità con cui una mano è stata scritta. Qualcuno conosce una lib utilizzando uno di questi strumenti per questo scopo?

Cosa mi è mancato qui?
Quale raccomanderesti?

Grazie.

Per la clonazione profonda (clona l’intera gerarchia di oggetti):

  • commons-lang SerializationUtils – using serialization – se tutte le classi sono sotto il tuo controllo e puoi forzare l’implementazione Serializable .

  • Java Deep Cloning Library – usando il reflection – nei casi in cui le classi o gli oggetti che vuoi clonare sono fuori dal tuo controllo (una libreria di terze parti) e non puoi renderli implementabili Serializable , o nei casi in cui non vuoi implementare Serializable .

Per clonazione superficiale (clona solo le proprietà di primo livello):

  • BeanUtils di fagioli commons-beanutils – nella maggior parte dei casi.

  • Spring BeanUtils – se si sta già utilizzando la molla e quindi si ha questa utilità sul classpath.

Ho deliberatamente omesso l’opzione “fai-da-te” – le API sopra forniscono un buon controllo su cosa e cosa non clonare (per esempio usando transient , o String[] ignoreProperties ), quindi non è preferibile reinventare la ruota.

Il libro di Joshua Bloch ha un intero capitolo intitolato “Item 10: Override Judedly Clone” in cui si cita perché il clone di override per la maggior parte è una ctriggers idea perché le specifiche Java creano molti problemi.

Fornisce alcune alternative:

  • Usa uno schema di fabbrica al posto di un costruttore:

      public static Yum newInstance(Yum yum); 
  • Utilizzare un costruttore di copie:

      public Yum(Yum yum); 

Tutte le classi di raccolta in Java supportano il costruttore di copie (ad es. Nuovo ArrayList (l);)

Dalla versione 2.07 Kryo supporta la clonazione superficiale / profonda :

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

Kryo è veloce, al e della loro pagina è ansible trovare un elenco di aziende che lo utilizzano in produzione.

Usa XStream toXML / fromXML in memoria. Estremamente veloce ed è in circolazione da molto tempo e sta andando forte. Non è necessario che gli oggetti siano serializzabili e non si usi la reflection (anche se XStream lo fa). XStream può distinguere variabili che puntano allo stesso object e non accidentalmente creare due copie complete dell’istanza. Un sacco di dettagli come quello sono stati martellati nel corso degli anni. L’ho usato per un certo numero di anni ed è un andare a. È facile da usare come puoi immaginare.

 new XStream().toXML(myObj) 

o

 new XStream().fromXML(myXML) 

Per clonare,

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

Più succintamente:

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

Dipende.

Per velocità, usa il fai da te. Per i proiettili, utilizzare la riflessione.

A proposito, la serializzazione non è la stessa di refl, poiché alcuni oggetti possono fornire metodi di serializzazione forzati (readObject / writeObject) e possono essere bacati

Consiglierei il modo fai-da-te che, combinato con un buon metodo hashCode () e equals () dovrebbe essere facile da provare in un test unitario.

Suggerirei di sovrascrivere Object.clone (), chiamare prima super.clone () e di chiamare ref = ref.clone () su tutti i riferimenti che si desidera copiare in profondità. È più o meno approccio da soli , ma ha bisogno di un po ‘meno di programmazione.

Per oggetti complicati e quando le prestazioni non sono significative, io uso gson per serializzare l’object in testo json, quindi deserializzare il testo per ottenere un nuovo object.

gson, che in base alla riflessione funziona nella maggior parte dei casi, con la differenza che i campi transient non verranno copiati e gli oggetti con riferimento circolare con 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); }