반응형
1️⃣ 왜 clone은 위험할까?
자바의 Cloneable 인터페이스와 Object.clone() 메서드는 객체의 복사를 쉽게 해줄 것 같지만, 실제로는 아님
- 복사가 얕게(shallow) 이루어짐
- CloneNotSupportedException 예외를 신경써야 함
- 상속 구조에서 불변식을 깨뜨리기 쉬움
즉, 잘못 사용하면 버그가 생기기 쉽고, 안전한 복제를 위해선 주의 깊은 설계가 필요함
2️⃣ 얕은 복사로 생기는 문제
class Person implements Cloneable {
String name;
List<String> phones;
@Override
public Person clone() {
try {
return (Person) super.clone(); // 얕은 복사
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
var p1 = new Person("Kim", new ArrayList<>(List.of("010-1111-2222")));
var p2 = p1.clone();
p2.phones.add("010-3333-4444");
여기서 p2.phonse 뿐만이 아니라 p1.phones도 같이 바뀜
이유는 - 두 객체가 같은 리스트를 참조했기 때문
super.clone()은 객체의 필드를 "비트 단위로 복사"
즉, 참조 타입 필드는 주소만 복사되어 동일한 객체를 가리킴
3️⃣ 안전한 clone 재정의 방법
가변 필드를 가진 객체를 안전하게 복사하려면 "깊은 복사(deep copy)"가 필요함
class Person implements Cloneable {
String name;
List<String> phones;
@Override
public Person clone() {
try {
Person copy = (Person) super.clone(); // 얕은 복사
copy.phones = new ArrayList<>(this.phones); // 리스트 깊은 복사
return copy;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
이렇게 하면 p1과 p2가 서로 다른 리스트를 갖게 되어,
한쪽의 변경이 다른 쪽에 영향을 주지 않게 됨
4️⃣ 배열이 있을 때는?
배열은 clone()을 호출하면 자체적으로 깊은 복사가 가능함
class Stack implements Cloneable {
private Object[] elements;
private int size;
@Override
public Stack clone() {
try {
Stack result = (Stack) super.clone();
result.elements = elements.clone(); // 배열 복사
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
단, 배열 원소가 참조 타입이라면 원소 자체는 여전히 공유됨
즉, 참조 타입이라면 원소 하나하나 clone을 해야함
5️⃣ 상속에서 더 위험한 이유
상속 구조에서 clone을 잘못 다루면 상위/하위 클래스 불변식이 깨질 수 있음
그래서 상속을 허용하는 클래스에서는 clone 사용을 피하는 것을 권장
필요하다면 final 클래스로 만들어서 clone 안정성 확보
6️⃣ clone 대신 권장되는 복사 방법
Cloneable보다는 복사 생성자나 복사 팩토리 메서드를 권장함
// 복사 생성자
Person(Person other) {
this.name = other.name;
this.phones = new ArrayList<>(other.phones);
}
// 복사 팩토리 메서드
static Person copyOf(Person other) {
return new Person(other);
}
이 방식은 다음과 같은 장점이 존재함
- 예외가 없음 (CloneNotSupportedException 불필요)
- 상속 문제 없음
- 명확하고 예측 가능한 동작
반응형
'Java & Spring Boot' 카테고리의 다른 글
| 🧩 이펙티브 자바 item 31. 한정적 와일드카드로 API 유연성을 높이라 (0) | 2025.10.17 |
|---|---|
| 🧩 이펙티브 자바 item 24. 멤버 클래스는 되도록 static으로 만들라 (0) | 2025.10.16 |
| Java의 ExecutorService 스레드 풀 정복하기 (0) | 2025.03.30 |
| Java Redis 직렬화 삼총사: Json, String, JDK (1) | 2024.11.10 |
| SSE와 WebSocket, 그들은 왜 실시간 통신의 라이벌이 되었을까? (6) | 2024.10.25 |