Kopiowanie obiektów

Bądźmy świadomi tego, że w przypadku obiektów, znak przypisania = nie dotyczy wartości obiektu, a jedynie jego referencji. Co to oznacza w rzeczywistości? Dwie zmienne mogą odnosić się do dokładnie tego samego miejsca w pamięci.
Co za tym idzie, modyfikacja jednego z nich prowadzi także do edycji drugiego. Tutaj pojawia się kilka problemów o których postaram się opowiedzieć w tym wpisie.

Wprowadzenie

W programowaniu czasami spotykamy się z sytuacją, gdzie dla poprawnego działania algorytmu musimy stworzyć dokładną kopię istniejącego już obiektu tylko po to, aby dokonać na nim jakieś zmiany, nie wpływając tym samym na nasz obiekt wejściowy. Okazuję się jednak, że nie jest to do końca tak proste, jak mogłoby się wydawać.

Rodzaje kopiowania

Okazuje się, że kopiowanie obiektów dzielimy na dwa rodzaje:
Kopiowanie płytkie ang. shallow copy
Kopiowanie głębokie ang. deep copy

Jaka jest różnica? W przypadku kopiowania płytkiego tworzymy nową instancję naszego obiektu z kopiami referencji w środku.
W przypadku głębokiego natomiast, tworzymy nową instancję obiektu wraz z nowymi instancjami w środku.
Skomplikowane i niejasno wytłumaczone? Spokojnie, zaraz postaramy się rozwiać wszystkie wątpliwości.

Kopiowanie płytkie

Na potrzeby dzisiejszego wpisu tworzymy sobie prostą klasę Car w której będziemy przechowywali trzy wartości: id, kolor samochodu oraz rok produkcji.

Stworzymy teraz prostą listę, która zawiera w sobie trzy instancje obiektów typu Car. Naszym pierwszym zadaniem na dzisiaj jest utworzenie niezależnej kopii tej listy.

Tworzymy więc drugą listę do której kopiujemy pierwszą oraz modyfikujemy kolor pierwszego z samochodów na biały.

Co się jednak okazuje, po modyfikacji koloru samochodu z listy drugiej uległ zmianie także ten w liście pierwszej.

Co poszło nie tak? Popełniliśmy podstawowy błąd: nie utworzyliśmy nowej listy, tylko skopiowaliśmy referencję do niej, przez co w rzeczywistości zarówno zmienna carList jak i copiedList pracują na dokładnie tej samej liście. Zabieramy się więc za poprawę naszego błędu.

Tworzymy więc kopię listy oraz kopiujemy do niej elementy z listy pierwszej.

Po uruchomieniu naszego programu jednak ciągle nie otrzymaliśmy wyniku, którego oczekiwaliśmy.

Co się stało? Faktycznie utworzyliśmy nową instancję Listy, okazuje się jednak, że zamiast skopiowania wartości, czyli utworzenia nowych obiektów, które w sobie zawiera, skopiowaliśmy jedynie nich referencję. Wychodzi więc na to, że pomimo tego, że lista sama w sobie jest nową instancją listy, to jej zawartość jest jedynie kopią referencji. Takie kopiowanie nazywamy kopiowaniem płytkim.

Kopiowanie głębokie

Aby poradzić sobie z takim problemem musimy skorzystać z mechanizmu klonowania. Aby tego dokonać musimy lekko rozbudować naszą klasę Car.

Jak widać troszkę się zmieniło. Zaimplementowaliśmy interfejs Cloneable. Nadpisaliśmy także metode clone() w taki sposób, aby utworzyła ona nowy obiekt typu Car, który zawiera w sobie wartości z naszego pierwowzoru. Możemy teraz łatwo przekopiować je do drugiej listy.

Ostatecznie udało się otrzymać to, co chcieliśmy otrzymać: zachowaliśmy oryginał naszego obiektu, oraz dowolnie zmodyfikowaliśmy jego kopię.

Czy to wszystko?

Odpowiedź brzmi nie, to nie koniec problemów. Kopiowanie głębokie nie musi przecież kończyć się na „drugim poziomie”. A co jeśli nasza klasa Car zawierałaby w sobie drugi obiekt, na przykład typu Engine?

Okazuje się wtedy, że dopisanie do naszej metody clone dodatkowego argumentu nie wystarcza.

Musimy wtedy dla każdego obiektu, który znajduje się „głębiej” także napisać metodę kopiującą oraz ją wywołać.

Głębokie kopiowanie odbywa się zatem poprawnie, nie zachowując w sobie żadnych referencji z poprzedniej listy.

Takim oto sposobem dotarliśmy do końca wpisu. Wiem, że kopiowanie sprawia kłopoty wielu osobom, które uczą się programowania. Sam pamiętam jak miałem z tym spore problemy gdy zaczynałem swoją przygodę z Javą. Zachęcam do napisania własnego kodu, ponieważ można napotkać tutaj na naprawdę sporo problemów.
Nowy wpis jak zawsze pojawi się w przyszłą niedzielę. Miłego wieczoru!

Your email address will not be published. Required fields are marked *

*