728x90
아이템17. 변경 가능성을 최소화하라
불변 불변 불변!! 불변 클래스 한 줄 요약
인스턴스 내부 값을 수정할 수 없는 클래스를 말한다.
불변 클래스는 생성될 때 불변 인스턴스에 간직된 정보는 고정되어 객체가 파괴되는 순간까지 절대 달라지지 않는다.
불변 클래스는 가변 클래스보다 설계하고 구현하고 사용하기 쉬우며, 오류가 생길 여지도 적고 훨씬 안전하다.
불변 클래스의 장점
- 가변 클래스보다 설계하고 구현하고 사용하기 쉽다.
- 단순하기 때문이다. 상태가 가변한 인스턴스는, 동일한 인스턴스를 활용하는 곳이 많아질수록 오류의 가능성이 늘어난다.
- 내가 사용하려던 인스턴스의 상태와 동일한지 확인을 하지 않는다면, 원하는 동작을 보장받을 수 없다.
- 불변 객체는 근본적으로 Thread Safe 하며 따로 동기화할 필요 없다.
- 여러 스레드가 동시에 사용해도 절대 훼손되지 않는다.
- 사실상 클래스를 Thread Safe하게 만드는 가장 쉬운 방법이기도 하다.
- 위의 이유로 불변 객체는 안심하고 공유할 수 있다.
- 따라서 불변 클래스라면 한번 만든 인스턴스를 최대한 재활용하기를 권장한다.
- 가장 쉬운 재활용 방법으로는 자주 쓰이는 값들을 상수(public static final)로 제공하는 것이다.
- 불변 클래스는 자주 사용되는 인스턴스를 캐싱하여 같은 인스턴스를 중복 생성하지 않게 해주는 정적 팩토리(아이템 1)를 제공할 수 있다.
- 방어적 복사를 할 필요도 없어진다.
- 아무리 복사해도 원본과 차이가 없기 때문에, 복사 자체가 의미가 없다.
- 그리하여 불변 클래스는 clone이나 복사 생성자를 제공하지 않는 것이 옳다.
- 불변 객체는 자유롭게 공유할 수 있음은 물론, 불변 객체끼리는 내부 데이터를 공유할 수 있다.
- 객체를 만들 때 다른 불변 객체들을 구성요소로 사용하면, 불변식을 유지하기 쉽다.
- 좋은 예로 불변 객체는 맵Map의 키와 집합Set의 원소로 쓰기에 안성 맞춤이다. 키가 바뀔 일이 없으니.
- 불변 클래스는 그 자체로 실패원자성을 제공한다.
- 상태가 절대 변하지 않으니 잠깐이라도 불일치 상태에 빠질 가능성이 없다.
불변 클래스의 단점
값이 다르면 반드시 독립된 객체로 만들어야 한다는 것이 단점으로 작용할 수 있다.
값이 가짓수가 많다면 이들을 모두 만드는데 큰 비용을 치뤄야 한다.
만약 백만 비트 짜리 BigInteger에서 비트 하나를 바꾸려면,
BigInteger moby = ...;
moby = moby.flipBit(0);
filpBit 메서드는 새로운 BigInteger 인스턴스를 생성한다. 원본가 단지 한 비트만 다른 백만 비트짜리 인스턴스를 말이다.
객체를 완성하기까지의 단계가 많고, 그 중간 단계에서 만든 객체들이 모두 버려진다면 성능 문제가 더 커진다.
단점을 극복하기 위해서
- 다단계 연산(multistep operation)을 예측하여 제공한다. 더 이상 각 단계마다 객체를 생성하지 않아도 된다. 예컨데 BigInteger는 모듈러 지수 같은 다단계 연산 속도를 높여주는 가변 동반 클래스(companion class)를 package-private로 두고 있다고 한다. 가변 동반 클래스를 구현하는 건 매우 어렵지만, BigInteger의 경우는 다행히 기구현되어 있다.
- 클라이언트가 원하는 복잡한 연산을 정확히 예측할 수 없다면 public으로 가변 동반 클래스를 제공하는 것이 최선이다. String의 String Builder가 그 예시이다.
불변 클래스를 만드는 방법
아래 다섯가지 규칙을 따르자.
- 객체의 상태를 변경하는 메서드(변경자)를 제공하지 않는다.
- 클래스를 확장할 수 없도록 한다.
- 모든 필드를 final로 선언한다.
- 모든 필드를 private로 선언한다.
- 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
이를 위해 단순하게 따를 수 있는 원칙은 모든 필드를 private final로 선언하는 것이다.
확장(상속)하지 못 하도록 하자
가장 쉬운 방법은 final class로 만드는 방법이지만, 더 유연한 방법으로는 모든 생성자를 private 혹은 package-private으로 만들고 public 정적 팩토리를 제공하는 방법이다. 바깥에서 볼 수 없는 package-private 구현 클래스를 원하는 만큼 만들어 활용할 수 있으니 유연하고, 패키지 바깥에서 클라이언트가 바라본 이 불변 객체는 사실상 final이므로 안전하다.
불변과 관련된 주의 사항
- 클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다.
- getter가 있다고 무조건 setter를 만들지 말자.
- 모든 클래스를 불변으로 만들수는 없다.
- 불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소한으로 줄이다.
- 생성자는 불변식 설정이 완료된, 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다.
- 확실한 이유가 없다면 생성자와 정적 팩토리 외에는 그 어떤 초기화 메서드도 public으로 제공해서는 안 된다.
- 객체를 재활용할 목적으로 상태를 다시 초기화하는 메서드도 안된다.
'도서 > 정리' 카테고리의 다른 글
[이펙티브 자바] 아이템21. 인터페이스는 구현하는 쪽을 생각해 설계하라 (0) | 2023.07.11 |
---|---|
[이펙티브 자바] 아이템20. 추상 클래스보다는 인터페이스를 우선하라 (0) | 2023.07.10 |
[이펙티브 자바] 아이템16. public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라 (0) | 2023.07.06 |
[이펙티브 자바] 아이템15. 클래스와 멤버의 접근 권한을 최소화하라 (0) | 2023.07.05 |
[이펙티브 자바] 아이템9. try-finally보다는 try-with-resources를사용하라 (0) | 2023.07.04 |