개발/JAVA

불변객체 만들기

beng9re 2022. 8. 29. 17:23

불변 객체에 대해 정리해보는 시간을 가져보도록 한다.

대부분의 내용은 이펙티브 자바와, 다른 분의 블로그를 인용하였습니다.

왜 불변 객체를 사용해야 할까?

  1. 불변 객체는 스레드의 안전하다.
  2. 생성과 동시에 값이 할당되고 변경이 불가능한 객체임으로 기본적으로 스레드의 안전하다.
  3. 실패 원자적인 메서드를 만들 수 있다.
  4. 이펙티브 자바에 따르면 메서드에서 예외가 발생한 후 에도 객체는 메서드 호출 전과 똑같은 유효한 상태이다. (내부 상태를 변경한 적이 없으니 이를 만족한다.
  5. 부수효과를 방지 할 수 있다.
  6. 객체 생성과 동시에 값이 할당되어 상태에 대한 변경이 내부에서도 이루어지지 않기 때문에 변경에 따른 부수효과를 방지할 수 있으며 이 부분은 사용시점에 가독성과 안정성을 향상한다.
  7. GC의 성능을 향상할 가능성이 있다.
  8. 불변 객체는 GC 대상을 Skip 할 수 있어 대상 탐색 시 성능상 유리하다고는 하는데 이 부분은 별도의 이해가 필요할 듯싶다.

단점은?

  1. 새로운 값이 필요할 때 새로운 객체를 생성해야 해서 이 부분에 메모리에 비효율성이 나타난다.
    • 데이터가 한정적인 경우 객체를 미리 생성하는 전략(캐싱)으로 어느 정도 비효율을 막을 수 있다.
      • LottoNumber을 미리 캐싱한다.
public class LottoNumber {
    static final int MIN_NUMBER = 1;
    static final int MAX_NUMBER = 45;
    private static Map<Integer, LottoNumber> lottoNumbers = new HashMap<>();

    private final int number;

    static {
        for (int i = MIN_NUMBER; i <= MAX_NUMBER; i++) {
            lottoNumbers.put(i, new LottoNumber(i));
        }
    }

    private LottoNumber(int number) {
        this.number = number;
    }

    public static LottoNumber of(int number) {
        LottoNumber lottoNumber = lottoNumbers.get(number);
        if (lottoNumber == null) {
            throw new IllegalArgumentException("로또의 숫자는" + MIN_NUMBER + "~" + MAX_NUMBER + "까지만 허용합니다.");
        }
        return lottoNumber;
    }

 

작성법

불변 객체는 아래에 규칙에 맞추어 작성한다.

  • 객체의 상태를 변경하는 메서드를 제공하지 않는다.
  • 클래스를 확장할 수 없도록 한다.
  • 모든 필드를 final로 선언한다.
  • 모든 필드를 private으로 선언한다.
  • 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
  • setter를 선언하지 않는다.

작성 예시 코드

  • 원시 타입만 있는 클래스
public class ImmutableData {

    private final int data;

    public ImmutableData(int data) {
        this.data = data;
    }

    public int getData() {
        return data;
    }
}
  • 참조 타입이 있는 클래스 (예시 컬랙션)
public class ImmutableCollection {

    private final List<String> data;

    public ImmutableCollection(final List<String> data) {
        this.data = List.copyOf(data);
    }

    public List<String> getData() {
        return data;
    }

    //java 8
    public List<String> getCollection() {
        return Collections.unmodifiableList(data);
    }
}

만약 Copy of가 없다면 컬랙션을 조회하고 add함수를 통해 값을 변경할 수 있음으로 이를 막기 위해 CopyOf 혹은 get 메서드에 Collections.unmodifiableList로 선언해둔다.

 

결론

오라클 공식문서에 따르면 “불변 객체는 당신이 고민하면서 만든 가변 객체보다 메모리를 더 효율적으로 적게 사용한다.”라고 한다.

그만큼 불변 객체가 충분히 적용할 수 있는 부분이라면 적극 적용하면 좋을 거 같다.

이펙티브 자바 아이템 17 항목처럼 적용이 불가능하다면 변경 가능서울 최소화 하는 편이 좋을 것 같다.