이전 글에서 final을 앞에 달아주기만 하면 무적의 불변 객체가 되 .... .는줄 알았으나 ....
불변 객체가 기본적으로 안전성을 높여주지만, 외부에서 참조하는 가변 객체를 포함하거나, 불변 객체의 일부 상태가 방어적 복사 없이 외부로 노출되는 경우 안전하지 않을 수 있다는 것. !!
그렇다면 불변 객체의 안전을 위협하는 상황에 대해서 알아보자
1. 가변 객체를 포함할 때
- 불변 객체 내부에 가변 객체가 포함되면, 해당 가변 객체의 상태가 외부에서 변경될 가능성이 생긴다. 예를 들어 Date 객체는 가변이므로, final 필드에 포함되더라도 객체 내부 상태가 바뀔 수 있다.
public final class Period {
private final Date startDate;
private final Date endDate;
public Period(Date startDate, Date endDate) {
this.startDate = new Date(startDate.getTime()); // 방어적 복사
this.endDate = new Date(endDate.getTime());
]
public Date getStartDate() {
return new Date(startDate.getTime()); // 방어적 복사
}
public Date getEndDate() {
return new Date(endDate.getTime());
}
}
만약 Date 객체를 방어적 복사 없이 그대로 할당하면, 외부에서 startDate 와 endDate 를 참조해서 상태를 변경할 수 있다. 이를 막으려면 생성자와 접근자에서 방어적 복사를 수행하는 것이 중요!!(별 다섯개)
2. 컬렉션과 같은 가변 데이터 구조를 포함할 때
- 만약 불변 객체가 List 나 Map 같은 컬렉션을 포함한다면, 이 컬렉션 역시 가변이기 때문에 외부에서 해당 컬렉션을 수정할 위험이 있다. 불변 객체는 불변 컬렉션을 사용하거나 방어적 복사를 통해 안전하게 컬렉션을 관리해야한다
- Collections.unmodifiableList() 와 같은 메서드를 사용해 변경 불가능한 뷰를 제공하거나, 전체 컬렉션을 복사하여 제공하는 방법이 있다.
3. 클래스 외부로 가변 객체를 반환할 때
- 불변 객체의 일부 데이터가 외부에 노출되어 외부에서 변경 가능한 경우, 불변성이 깨질 수 있다. 내부 필드를 그대로 반환하는 대신 새로운 객체를 생성해 반환하거나 불변 객체로 반환하는 것이 안전하다.
public final class Person {
private final String name;
private final List<String> friends; // 친구 목록 (가변 객체)
public Person(String name, List<String> friends) {
this.name = name;
this.friends = friends; // 원본 리스트를 그대로 할당
}
public String getName() {
return name;
}
public List<String> getFriends() {
return friends; // 가변 객체를 직접 반환
}
}
// 불변 객체의 불변성이 깨지는 예시
public class Main {
public static void main(String[] args) {
List<String> friends = new ArrayList<>();
friends.add("Alice");
Person person = new Person("John", friends);
person.getFriends(); // [Alice]
person.getFriends().add("Bob");
person.getFriends(); // [Alice, Bob]
}
}
// 불변 객체의 불변성을 유지하는 방법
public final class Person {
private final String name;
private final List<String> friends;
public Person(String name, List<String> friends) {
this.name = name;
this.friends = new ArrayList<>(friends); // 방어적 복사
}
public String getName() {
return name;
}
public List<String> getFriends() {
return Collections.unmodifiableList(friends); // 불변 리스트 반환
}
}
4. 직렬화할 때
- 불변 객체가 직렬화된 경우, 역직렬화 시에 공격자가 생성자를 우회해 내부 상태를 변경할 수 있다. 이를 막기 위해 직렬화된 객체가 올바른 형태로 재생성되도록 readResolve() 메서드를 사용해 불변성을 유지해야 한다.
'Java > 기본' 카테고리의 다른 글
[Java] CompletableFuture (0) | 2024.11.08 |
---|---|
[Java] Future (0) | 2024.11.08 |
[Java] Java에서 final로 불변 객체 만들기 (0) | 2024.10.30 |
[Java] 스트림(Stream) (0) | 2024.10.27 |
[Java] 표준 함수형 인터페이스 (0) | 2024.10.27 |