velog 에서 홍보를 보고 매일메일이라는 뉴스레터를 구독하게 되었다.
이 뉴스레터는 매일 개발 면접 질문을 보내 주는데, 백엔드와 프론트엔드로 나누어 매일 보내 준다.
풀스택을 지향하고 있는 나는 둘 다 구독하지만, 역시 조금이라도 더 알고 있는 백엔드의 CS 에 조금 더 관심이 가더라.
꼭 면접 때문이 아니더라도 (현재 현업 중) 면접 질문은 CS 에 대한 갈망을 조금이나마 풀어주는 것 같다. 그리고 이건! 취업 전 들여다보던 얕은 복사와 깊은 복사를 간단히 정리해 보는 포스팅이다. 오랜만에 보는 CS가 재미있기 때문에....
먼저 코드를 보자.
class Book {
private String name; // 책 이름
private Author author; // 저자
public Book(String name, Author author) {
this.name = name;
this.author = author;
}
public Book shallowCopy() { // 얕은 복사
return new Book(this.name, this.author);
}
public Book deepCopy() { // 깊은 복사
Author copiedAuthor = new Author(this.author.getName());
return new Book(this.name, copiedAuthor);
}
public void changeAuthor(String name) { // 저자 이름 변경
author.setName(name);
}
@Override
public String toString() {
return "Book name : " + name + ", " + author;
}
static class Author {
private String name; // 저자 이름
public Author(String name) {
this.name = name;
}
public String getName() { // 저자 이름 반환
return name;
}
public void setName(String name) { // 저자 이름 변경
this.name = name;
}
@Override
public String toString() {
return "Author : " + name;
}
}
public static void main(String[] args) {
Author author1 = new Author("조슈아 블로크");
Book book1 = new Book("이펙티브 자바", author1);
// 얕은 복사 후 변경
Book shallowCopyBook = book1.shallowCopy();
shallowCopyBook.changeAuthor("Joshua Bloch");
// 얕은 복사 결과 출력
System.out.println("After shallow copy and change:");
System.out.println("Original book1: " + book1);
System.out.println("Shallow copied book: " + shallowCopyBook);
Author author2 = new Author("마틴 파울러");
Book book2 = new Book("리팩터링", author2);
// 깊은 복사 후 변경
Book deepCopyBook = book2.deepCopy();
deepCopyBook.changeAuthor("Martin Fowler");
// 깊은 복사 결과 출력
System.out.println("
After deep copy and change:");
System.out.println("Original book2: " + book2);
System.out.println("Deep copied book: " + deepCopyBook);
}
}
코드를 보면, this 를 통해 shallow 얕은 복사는 그 객체 자신을 "참조" 한다.
즉, 복사하여 새로운 것을 만들어 내는 것이 아니라 원본을 가진 값을 만들어내는 것이다.
그러므로 만약 얕은 복사를 한 객체 참조 값이 변경된다? -> 실제 값도 변경이라는 결과
겉보기에는 복사된 것 같지만 실제로는 같은 객체를 공유하는 것이기 때문이다.
Book shallow = original.shallowCopy();
shallow.changeAuthor("바뀐 이름"); // → original 도 같이 바뀜
반면 깊은 복사는 getBook 메서드를 통해, 해당 값을 복사하여 완전히 새로운 객체를 탄생시킨다.
완전히 별개의 객체로 존재하기 때문에, 복사본에서 값을 변경해도 원본에는 영향이 없다.
Book deep = original.deepCopy();
deep.changeAuthor("바뀐 이름"); // → original 은 그대로
이렇게 설명만 하면 알아듣는다. 그런데 이건 귀납적인 설명이고, 내가 항상 헷갈리고야 말았던 부분은, 아래와 같다.
왜 생성자 new 를 쓰는데도 새 객체가 아니라 본 객체를 참조하는 복사본인 거지?
public Book shallowCopy() {
return new Book(this.name, this.author);
}
여기서 new Book(...) 은 Book 객체를 새로 만들고 있다. 즉, Book 객체 자체는 새로운 인스턴스가 맞다.
그러나 이 안에 들어가는 Author 는, this.author = 즉, 기존 걸 그대로 재사용 하는 Author 인 것이다.
그래서 shallowCopy 를 할 경우 Author 값은 영향을 받는다.
여기서 또 짚고 넘어갈 점은, 같은 원리로 Book 객체의 name 을 변경하는 건 깊은 복사이기 때문에 name 을 새로 하는 건 원본에 영향을 안 끼친다.
이상이다.