MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Tipurile de referință și copierea obiectelor

În Java, trebuie să fim conștienți că toate obiectele claselor reprezintă tipuri de referință, ceea ce înseamnă că indică un obiect situat în memorie. Pentru a înțelege posibilele dificultăți, să analizăm următorul exemplu:

public class Program{
     
   public static void main(String[] args) {
         
       Person tom = new Person("Tom", 23);
       tom.display();      // Person Tom
       Person bob = tom;
       bob.setName("Bob");
       tom.display();      // Person Bob
   }
}
class Person{
   private String name;
   private int age;
   
   Person(String name, int age){
       this.name = name;
       this.age = age;
   }
   void setName(String name){
       this.name = name;
   }
   void setAge(int age){
       this.age = age;
   }
   void display(){
       System.out.printf("Person Name: %s \n", name);
   }
}

Aici creăm două obiecte Person, dar le atribuim unul celuilalt. Deși schimbăm doar obiectul bob, și tom se modifică. Aceasta se întâmplă deoarece, după atribuirea bob = tom, ambele variabile indică aceeași zonă de memorie, unde sunt stocate datele obiectului Person.

Pentru a evita această problemă, trebuie să creăm un obiect separat pentru variabila bob, de exemplu, folosind metoda clone:

class Person implements Cloneable {
   private String name;
   private int age;
   
   Person(String name, int age){
       this.name = name;
       this.age = age;
   }
   void setName(String name){
       this.name = name;
   }
   void setAge(int age){
       this.age = age;
   }
   void display(){
       System.out.printf("Person %s \n", name);
   }
   
   public Person clone() throws CloneNotSupportedException {
       return (Person) super.clone();
   }
}

Pentru a implementa clonarea, clasa Person trebuie să implementeze interfața Cloneable, care definește metoda clone. Implementarea acestei metode returnează apelul metodei clone din clasa părinte, Object, cu conversia la tipul Person.

De asemenea, pentru cazul în care clasa nu acceptă clonarea, metoda ar trebui să arunce o excepție CloneNotSupportedException, folosind operatorul throws.

Apoi, putem copia obiectul utilizând această metodă:

try {
   Person tom = new Person("Tom", 23);
   Person bob = tom.clone();
   bob.setName("Bob");
   tom.display();      // Person Tom
} catch (CloneNotSupportedException ex) {
   System.out.println("Cloneable not implemented");
}

Cu toate acestea, această metodă realizează o copiere superficială și este adecvată dacă obiectul clonat nu conține obiecte complexe. De exemplu, dacă avem clasa Book definită astfel:

class Book implements Cloneable {
   private String name;
   private Author author;
   
   public void setName(String n){ name = n; }
   public String getName(){ return name; }
   
   public void setAuthor(String n){ author.setName(n); }
   public String getAuthor(){ return author.getName(); }

   Book(String name, String author) {
       this.name = name;
       this.author = new Author(author);
   }
   
   public String toString() {
       return "Cartea '" + name + "' (autor " +  author + ")";
   }
   
   public Book clone() throws CloneNotSupportedException {
       return (Book) super.clone();
   }
}

class Author {
   private String name;
   
   public void setName(String n){ name = n; }
   public String getName(){ return name; }
   
   public Author(String name) {
       this.name = name;
   }
}

Dacă încercăm să schimbăm autorul cărții, ne vom confrunta cu o problemă:

try {
   Book book = new Book("War and Peace", "Leo Tolstoy");
   Book book2 = book.clone();
   book2.setAuthor("Ivan Turgenev");
   System.out.println(book.getAuthor());
} catch (CloneNotSupportedException ex) {
   System.out.println("Cloneable not implemented");
}

În acest caz, deși variabilele book și book2 vor indica obiecte diferite în memorie, aceste obiecte vor indica același obiect Author.

În acest caz, trebuie să realizăm o copiere completă (deep copy). Pentru aceasta, trebuie să definim metoda clone în clasa Author:

class Author implements Cloneable {
   // restul codului clasei
   
   public Author clone() throws CloneNotSupportedException {
       return (Author) super.clone();
   }
}

Apoi, vom modifica metoda clone în clasa Book astfel:

public Book clone() throws CloneNotSupportedException {
   Book newBook = (Book) super.clone();
   newBook.author = (Author) author.clone();
   return newBook;
}

Acum, metoda clone din clasa Book creează o copie completă, asigurându-se că și obiectul Author este copiat în mod corespunzător.

← Lecția anterioară Lecția următoare →