자바에서 객체 복사는 기존의 인스턴스와 동일한 상태를 가진 새로운 객체를 생성하는 과정입니다. 역사적으로 객체를 수동으로 다시 생성하지 않고 복제해야 할 필요성이 제기되면서 자바의 초기 발전 단계에서 이 복사 개념이 등장했습니다. 자바는 객체 복사를 위한 보편적인 방법을 제공하지 않으며, 대신 clone() 계약과 복사 생성자 패턴을 사용합니다.
clone() 메소드는 객체 클로닝을 위한 표준 메커니즘을 제공하기 위해 Cloneable 인터페이스에 도입되었으나, 그 구현은 많은 뉘앙스를 제공하며 종종 버그를 발생시킵니다.
주요 문제는 "상자 속"에서 진정한 깊은 복사가 없고, 얕은 클로닝(shallow copy) 시 예측할 수 없는 행동이 발생할 가능성입니다. 복사 생성자는 얕은 복사와 깊은 복사를 모두 구현할 수 있지만 명시적인 구현이 필요합니다.
복잡한 객체를 안전하고 올바르게 복사하려면 복사 생성자 또는 팩토리 메소드를 사용하는 것이 더 좋으며, clone()은 객체가 이 계약에 실제로 적합한 경우에만 주의해서 사용하는 것이 좋습니다.
코드 예시:
// 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(); // 깊은 복사 address return copy; } } // 복사 생성자 public class Person { String name; Address address; public Person(Person other) { this.name = other.name; this.address = new Address(other.address.city); } }
주요 특징:
clone()은 Cloneable 인터페이스의 구현과 clone 메소드의 재정의를 요구합니다.질문 1: 클래스가 Cloneable을 구현하지 않으면 clone()을 호출할 때 어떤 일이 발생합니까?
clone()은 객체가 Cloneable 인터페이스를 구현하지 않으면 CloneNotSupportedException을 던집니다.
질문 2: clone()을 통한 클로닝은 기본적으로 깊은 복사인가요?
아니요, 기본적으로 얕은 클로닝만 구현되며, 모든 객체에 대한 참조는 그대로 복사됩니다.
질문 3: 깊은 클로닝을 위해 복사 생성자를 사용하지 않고도 될까요?
아니요, 프로세스를 제어하려면 명시적으로 생성자를 통해 내부 객체들을 복사하거나 clone()에서 수동으로 구현해야 합니다.
개발자가 List<Product> 필드를 가진 Order 클래스에서 표준 clone()을 적용했습니다. 그 결과 복사본에서 제품 목록을 변경하면 원본의 제품도 변경되어 버그가 발생했습니다.
장점:
단점:
회사는 모든 도메인 엔티티에 대해 복사 생성자를 구현하고, 복잡한 중첩 객체의 클로닝 프로세스에 대한 테스트를 추가했습니다.
장점:
단점: