Die Kopie von Objekten in Java ist der Prozess, ein neues Objekt mit demselben Zustand wie ein bestehendes Exemplar zu erstellen. Historisch wurde das Kopieren seit den Anfängen von Java relevant, als die Notwendigkeit entstand, Objekte zu duplizieren, ohne sie manuell neu zu erstellen. Java selbst bietet keinen universellen Weg zum Kopieren von Objekten, sondern nutzt stattdessen den Vertrag clone() und das Muster des Kopierkonstruktors.
Die Methode clone() wurde im Interface Cloneable eingeführt, um einen standardisierten Mechanismus zum Klonen von Objekten zu bieten. Ihre Implementierung birgt jedoch viele Nuancen und führt häufig zu Fehlern.
Das Hauptproblem ist die Abwesenheit einer echten tiefen Kopie "out of the box" und die Möglichkeit von unvorhersehbarem Verhalten bei flachem Klonen (shallow copy). Der Kopierkonstruktor ermöglicht sowohl flaches als auch tiefes Kopieren, erfordert jedoch eine explizite Implementierung.
Für sicheres und korrektes Kopieren komplexer Objekte ist es vorzuziehen, den Kopierkonstruktor oder die Fabrikmethode zu verwenden, während clone() mit Vorsicht angewendet werden sollte — nur wenn das Objekt tatsächlich für diesen Vertrag geeignet ist.
Codebeispiel:
// Flaches Klonen über 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(); // tiefe Kopie von address return copy; } } // Kopierkonstruktor public class Person { String name; Address address; public Person(Person other) { this.name = other.name; this.address = new Address(other.address.city); } }
Wichtige Merkmale:
clone() erfordert die Implementierung des Interfaces Cloneable und die Überschreibung der Methode clone.Frage 1: Was passiert, wenn die Klasse Cloneable nicht implementiert?
clone() wirft CloneNotSupportedException, wenn das Objekt das Interface Cloneable nicht implementiert.
Frage 2: Ist das Klonen über clone() standardmäßig tief?
Nein, standardmäßig ist nur flaches Klonen implementiert, alle Referenzen auf Objekte werden unverändert kopiert.
Frage 3: Kann man auf einen Kopierkonstruktor für tiefes Klonen verzichten?
Nein, wenn Sie den Prozess kontrollieren möchten, müssen Sie das Kopieren verschachtelter Objekte explizit im Konstruktor oder manuell in clone() angeben.
Ein Entwickler hat den Standard clone() in der Klasse Order mit dem Feld List<Product> verwendet. Infolgedessen änderten sich beim Ändern der Produktliste in der Kopie auch die Produkte im Original, was einen Bug auslöste.
Vorteile:
Nachteile:
Im Unternehmen wurde ein Kopierkonstruktor für alle Domain-Entities implementiert und der Klonprozess für komplexe verschachtelte Objekte durch Tests abgedeckt.
Vorteile:
Nachteile: