La copia degli oggetti in Java è il processo di creazione di un nuovo oggetto con lo stesso stato di un'esemplare esistente. Storicamente, la copia è diventata rilevante sin dagli inizi dello sviluppo di Java, quando è emersa la necessità di duplicare oggetti senza ricrearli manualmente. Java stesso non fornisce un modo universale per copiare oggetti, ma utilizza invece il contratto clone() e il pattern del costruttore di copia.
Il metodo clone() è stato introdotto nell'interfaccia Cloneable per fornire un meccanismo standard per la clonazione degli oggetti, ma la sua implementazione presenta molte sfide e spesso porta all'insorgere di bug.
Il problema principale è l'assenza di una vera copia profonda 'out of the box' e la possibilità di comportamenti imprevedibili durante la clonazione superficiale (shallow copy). Il costruttore di copia permette di implementare sia la copia superficiale che quella profonda, ma richiede un'implementazione esplicita.
Per una copia sicura e corretta di oggetti complessi, è preferibile utilizzare il costruttore di copia o un metodo factory, mentre clone() dovrebbe essere usato con cautela, solo se l'oggetto si adatta veramente a questo contratto.
Esempio di codice:
// Clonazione superficiale tramite clone() public class Address implements Cloneable { String city; public Address(String city) { this.city = city; } @Override public Address clone() throws CloneNotSupportedException { return (Address) super.clone(); } } public class Person implements Cloneable { String name; Address address; public Person(String name, Address address) { this.name = name; this.address = address; } @Override public Person clone() throws CloneNotSupportedException { Person copy = (Person) super.clone(); copy.address = address.clone(); // copia profonda address return copy; } } // Costruttore di copia public class Person { String name; Address address; public Person(Person other) { this.name = other.name; this.address = new Address(other.address.city); } }
Caratteristiche chiave:
clone() richiede l'implementazione dell'interfaccia Cloneable e la sovrascrittura del metodo clone.Domanda 1: Se la classe non implementa Cloneable, cosa succede quando si chiama clone()?
clone() lancerà CloneNotSupportedException se l'oggetto non implementa l'interfaccia Cloneable.
Domanda 2: La clonazione tramite clone() è profonda per default?
No, per default viene implementata solo la clonazione superficiale, tutti i riferimenti agli oggetti vengono copiati così come sono.
Domanda 3: Si può fare a meno del costruttore di copia per una clonazione profonda?
No, se si vuole controllare il processo, è necessario specificare esplicitamente la copia degli oggetti annidati tramite il costruttore o manualmente in clone().
Uno sviluppatore ha utilizzato il clone() standard nella classe Order con un campo List<Product>. Di conseguenza, quando la lista dei prodotti è stata modificata nella copia, anche i prodotti nell'originale sono cambiati, causando un bug.
Vantaggi:
Svantaggi:
In azienda è stato implementato un costruttore di copia per tutte le entità di dominio, e sono stati coperti test per il processo di clonazione di oggetti annidati complessi.
Vantaggi:
Svantaggi: