방어적 복사
외부에서 전달받은 객체나 내부 객체를 반환할 때, 원본이 예기치 않게 변경되는 것을 막기 위해 복사본을 만들어 사용하는 기법입니다.
왜 필요한가?
public class Period {
private final Date start;
private final Date end;
public Period(Date start, Date end) {
this.start = start;
this.end = end;
}
public Date getStart() { return start; }
}
// 외부에서 원본 변경 가능
Date start = new Date();
Period period = new Period(start, new Date());
start.setTime(0L); // period 내부 start도 변경됨!
방어적 복사 적용
public class Period {
private final Date start;
private final Date end;
// 생성자 - 입력값 복사
public Period(Date start, Date end) {
this.start = new Date(start.getTime()); // 방어적 복사 O
this.end = new Date(end.getTime());
}
// getter - 반환값 복사
public Date getStart() {
return new Date(start.getTime()); // 방어적 복사 O
}
}
// 이제 외부에서 변경 불가
Date start = new Date();
Period period = new Period(start, new Date());
start.setTime(0L); // period 내부엔 영향 없음 O
적용 시점
| 시점 | 설명 |
| 생성자 입력 | 외부 객체가 내부로 들어올 때 복사 |
| getter 반환 | 내부 객체가 외부로 나갈 때 복사 |
얕은 복사 vs 깊은 복사
| 얕은 복사 | 깊은 복사 |
| 복사 범위 | 1단계만 복사 | 중첩 객체까지 모두 복사 |
| 방어적 복사 | 단순 객체에 적합 | 중첩 객체에 필요 |
// 얕은 복사 - 내부 객체는 여전히 공유
List<int[]> copy = new ArrayList<>(original);
// 깊은 복사 - 내부까지 모두 복사 O
List<int[]> deepCopy = original.stream()
.map(arr -> arr.clone())
.collect(Collectors.toList());
방어적 복사 vs 불변 객체
| 방어적 복사 | 불변 객체 |
| 방법 | 복사본 생성 | 아예 변경 불가 |
| 비용 | 복사 비용 발생 | 없음 |
| 권장 | 가변 객체 사용 시 | 가능하면 우선 고려 |
// 불변 객체 사용 시 방어적 복사 불필요
// LocalDateTime은 불변 객체
public class Period {
private final LocalDateTime start; // 방어적 복사 필요 없음 O
}
가장 좋은 방법은 불변 객체(Immutable Object)를 사용하는 것이며, 가변 객체를 써야 할 때 방어적 복사로 보완합니다.