Introduzione
Quando vogliamo copiare un oggetto in Java, ci sono due possibilità che dobbiamo considerare: una copia superficiale e una copia profonda.
La copia superficiale è l’approccio quando copiamo solo i valori dei campi e quindi la copia potrebbe dipendere dall’oggetto originale. Nell’approccio deep copy, ci assicuriamo che tutti gli oggetti nell’albero siano copiati profondamente, quindi la copia non dipende da alcun oggetto esistente precedente che potrebbe mai cambiare.,
In questo articolo, confronteremo questi due approcci e impareremo quattro metodi per implementare la copia profonda.
Ulteriori letture:
Java Copy Constructor
Copiare set in Java
la Copia di un HashMap in Java
Maven Installazione
useremo tre Maven dipendenze — Ediletto, Jackson, e Apache Commons Lang — per testare i diversi modi di eseguire una copia profonda.
Aggiungiamo queste dipendenze al nostro pom.xml:
Le ultime versioni di Gson, Jackson e Apache Commons Lang possono essere trovate su Maven Central.,
Modello
Per confrontare diversi metodi per copiare gli oggetti Java, avremo bisogno di due classi per lavorare su:
class Address { private String street; private String city; private String country; // standard constructors, getters and setters}
class User { private String firstName; private String lastName; private Address address; // standard constructors, getters and setters}
Copia Superficiale
Una copia superficiale è quello in cui abbiamo solo copiare i valori dei campi di un oggetto all’altro:
In questo caso il pm != shallowCopy, il che significa che sono oggetti diversi, ma il problema è che quando cambiamo una delle proprietà dell’indirizzo originale, questo influenzerà anche l’indirizzo di shallowCopy.,
Non ci preoccuperemmo se l’indirizzo fosse immutabile, ma non lo è:
Deep Copy
Una copia profonda è un’alternativa che risolve questo problema. Il suo vantaggio è che almeno ogni oggetto mutabile nel grafico dell’oggetto viene copiato ricorsivamente.
Poiché la copia non dipende da alcun oggetto mutabile creato in precedenza, non verrà modificato per caso come abbiamo visto con la copia superficiale.
Nelle sezioni seguenti, mostreremo diverse implementazioni di deep copy e dimostreremo questo vantaggio.
5.1., Copy Constructor
La prima implementazione che implementeremo è basata sui costruttori di copia:
public Address(Address that) { this(that.getStreet(), that.getCity(), that.getCountry());}
public User(User that) { this(that.getFirstName(), that.getLastName(), new Address(that.getAddress()));}
Nell’implementazione precedente della copia profonda, non abbiamo creato nuove stringhe nel nostro costruttore di copia perché String è una classe immutabile.
Di conseguenza, non possono essere modificati accidentalmente. Vediamo se funziona:
5.2. Interfaccia clonabile
La seconda implementazione è basata sul metodo clone ereditato dall’Oggetto. E ‘ protetto, ma dobbiamo ignorarlo come pubblico.,
Aggiungeremo anche un’interfaccia marker, Clonabile, alle classi per indicare che le classi sono effettivamente clonabili.
Aggiungiamo il metodo clone() alla classe Indirizzo:
E ora implementiamo clone() per la classe Utente:
Si noti che il super.la chiamata clone () restituisce una copia superficiale di un oggetto, ma impostiamo manualmente copie profonde di campi mutabili, quindi il risultato è corretto:
Librerie esterne
Gli esempi precedenti sembrano facili, ma a volte non si applicano come soluzione quando non possiamo aggiungere un costruttore aggiuntivo o sovrascrivere il metodo clone.,
Questo potrebbe accadere quando non possediamo il codice, o quando il grafico dell’oggetto è così complicato che non finiremmo il nostro progetto in tempo se ci concentrassimo sulla scrittura di costruttori aggiuntivi o sull’implementazione del metodo clone su tutte le classi nel grafico dell’oggetto.
E allora? In questo caso, possiamo usare una libreria esterna. Per ottenere una copia profonda, possiamo serializzare un oggetto e quindi deserializzarlo in un nuovo oggetto.
Diamo un’occhiata ad alcuni esempi.
6.1., Apache Commons Lang
Apache Commons Lang ha SerializationUtils#clone, che esegue una copia profonda quando tutte le classi nel grafico degli oggetti implementano l’interfaccia serializzabile.
Se il metodo incontra una classe che non è serializzabile, fallirà e genererà una SerializationException non selezionata.
A causa di ciò, dobbiamo aggiungere l’interfaccia serializzabile alle nostre classi:
6.2. Serializzazione JSON Con Gson
L’altro modo per serializzare è usare la serializzazione JSON. Gson è una libreria utilizzata per convertire oggetti in JSON e viceversa.,
A differenza di Apache Commons Lang, GSON non ha bisogno dell’interfaccia serializzabile per effettuare le conversioni.
Diamo un rapido sguardo ad un esempio:
6.3. JSON Serializzazione Con Jackson
Jackson è un’altra libreria che supporta la serializzazione JSON. Questa implementazione sarà molto simile a quella che usa Gson, ma dobbiamo aggiungere il costruttore predefinito alle nostre classi.
Vediamo un esempio:
Conclusione
Quale implementazione dovremmo usare quando facciamo una copia profonda?, La decisione finale dipenderà spesso dalle classi che copieremo e se possediamo le classi nel grafico dell’oggetto.
Come sempre, gli esempi di codice completi per questo tutorial possono essere trovati su GitHub.
Inizia con Spring 5 e Spring Boot 2, attraverso il corso Learn Spring:
> > DAI UN’OCCHIATA AL CORSO