불변 클래스 : 인스턴스의 내부 값을 수정할 수 없는 클래스
클래스를 불변으로 설계하는 이유 : 가변 클래스보다 설계하고 구현하고 사용하기 쉽고 오류가 생길 여지가 적어 안전하다.
클래스를 불변으로 만드는 규칙 :
- 객체의 상태를 변경하는 메서드(변경자)를 제공하지 않는다.
- setter 메서드를 제공하지 않아야한다. 객체의 상태는 생성 시점에만 설정되고, 이후에는 변경될 수 없다.
- 클래스를 확장할 수 없도록 한다.
- 상속을 통해 하위 클래스에서 부주의하게 객체의 상태를 변경하는 사태를 막아준다.
- 모든 필드를 final로 선언한다.
- 필드가 생성자에서 한 번만 할당될 수 있고, 이후에는 그 값이 변경될 수 없다.
- 모든 필드를 private으로 선언한다.
- private으로 선언함으로써 클래스 외부에서 직접 접근할 수 없게한다.
- 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
- 클래스에 가변 객체를 참조하는 필드가 하나라도 있다면 클라이언트에서 그 객체의 참조를 얻을 수 없도록 해야 한다. 가변 객체를 참조하는경우 해당 참조를 직접 노출하지 않고 방어적 복사를 통해 반환해야 한다.
이 다섯가지 규칙을 모두 활용해 만든 예시 코드이다 :
public final class ImmutableClass {
private final String name; // 3. 모든 필드는 final
private final int value;
private final List<String> mutableList; // 가변 컴포넌트
// 생성자에서 모든 필드를 초기화
public ImmutableClass(String name, int value, List<String> mutableList) {
this.name = name;
this.value = value;
// 5. 내부 가변 컴포넌트의 방어적 복사본 생성
this.mutableList = new ArrayList<>(mutableList);
}
// 1. 변경자 메서드 없음
// 접근자 메서드는 필드의 값만 반환하고, 가변 필드의 경우 방어적 복사본을 반환
public String getName() {
return name;
}
public int getValue() {
return value;
}
public List<String> getMutableList() {
// 가변 필드에 대한 방어적 복사본 반환
return new ArrayList<>(mutableList);
}
}
결론 : 클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다. 하지만 모든 클래스를 불변으로 만들 수는 없다. 불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소한으로 줄이자. 다른 합당한 이유가 없다면 모든 필드는 private final 이어야 한다. 생성자는 불변식 설정이 모두 완료된, 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다. 확실한 이유가 없다면 생성자와 정적 팩터리 메서드 외에는 그 어떤 초기화 메서드도 public으로 제공해서는 안된다.
'이펙티브 자바' 카테고리의 다른 글
[이펙티브 자바] 아이템 19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라 (0) | 2024.03.21 |
---|---|
[이펙티브 자바] 아이템 18. 상속보다는 컴포지션을 사용하라 (0) | 2024.03.21 |
[이펙티브 자바] 아이템 16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 (0) | 2024.03.18 |
[이펙티브 자바] 아이템 15. 클래스와 멤버의 접근 권한을 최소화하라 (0) | 2024.03.18 |
[이펙티브 자바] 아이템 14. Comparable을 구현할지 고려하라 (0) | 2024.03.14 |