방어적 복사가 필요한 이유
객체 내에 List 같은 Collections 객체를 필드로 가지면 final로 선언해도 외부에서 .add로 조작이 가능하다. 이런 조작을 방어하기 위해 방어적 복사가 필요하다.
public class Store {
private final String name;
private final List<Item> items;
public Store(String name, List<Item> items) {
this.name = name;
this.items = items; // 외부 리스트를 그대로 받음
}
public List<Items> getItems() {
return items; // 내부 리스트를 그대로 반환
}
public void addItems(Item item) {
if (checkSafe(item)) items.add(item); // 안전 체크
}
}
위 코드에서 새 item을 items에 추가하기 위해서 checkSafe()라는 함수를 거쳐야 하도록 의도되어 있다.
List<Item> items = new ArrayList<>();
Store drugStore = new Store("약국", items);
items.add(불법약품); // 외부에서 내부 상태 조작 가능
drugStore.getItems().add(불법약품); // getter로 받은 리스트도 그대로 조작 가능
하지만 위와 같은 방법으로 addItems()를 거치지 않고 items에 item을 추가해버릴 수 있다.
입구를 막는 방어 - 생성자 복사
public Store(String name, List<Item> items) {
this.name = name;
this.items = new ArrayList<>(items);
}
위와 같이 생성자에서 새 리스트로 복사해서 가져오면 items.add(불법약품)를 해도 내부에 영향이 없다.
출구를 막는 방어 - getter에서 읽기 전용 뷰
public List<Item> getItems() {
return Collections.unmodifiableList(items);
}
위와 같이 코드하면, 호출자가 drugStore.getItems().add(불법약품) 같이 호출하면 UnsupportedOperationException 에러가 발생한다.
방어적 복사를 위한 가장 깔끔한 방법 (Java 10+) - List.copyOf()
public Customer(String name, List<Item> items) {
this.name = name;
this.items = List.copyOf(items); // 불변 복사본 (null도 거부)
}
public List<Item> getItems() {
return items; // 이미 불변이라 그대로 반환해도 안전
}'Java > 객체 지향' 카테고리의 다른 글
| 정적 팩토리 메서드 (0) | 2026.06.15 |
|---|---|
| 상속(Inheritance) VS 합성(Composition) (0) | 2026.06.15 |
| 값 객체(Value Object)와 불변성 (0) | 2026.06.12 |
| 의존성 주입(Dependency Injection)과 조립자 (0) | 2026.06.12 |
| 객체 지향 원칙 4 - 다형성(Polymorphism) (0) | 2026.06.04 |