17. 변경 가능성을 최소화하라

신명철·2022년 2월 16일
0

Effective Java

목록 보기
15/80

불변 클래스

인스턴스의 내부 값을 수정할 수 없는 클래스로 불변 인스턴스에 간직된 정보는 고정되어 객체가 파괴되는 순간까지 절대 달라지지 않는다.

불변 클래스를 만들기 위한 법칙

  1. 객체의 상태를 변경하는 메서드를 제공하지 않는다.
  2. 클래스를 확장할 수 없도록 한다. 즉, 상속하지 못하도록 한다.
  3. 모든 필드를 final 로 선언한다.
  4. 모든 필드를 private 로 선언한다.
  5. 자신 외 내부 가변 컴포넌트에 접근할 수 없도록 한다. 가변 객체를 참조하는 필드가 하나라도 있다면 방어적 복사를 통해 반환하라

불변 클래스 예시

package com.java.effective.chapter4.item17;

public final class Complex {
    private final double re;
    private final double im;
    public static final Complex ZERO = new Complex(0, 0);
    public static final Complex ONE = new Complex(1, 0);
    public static final Complex I = new Complex(0, 1);

    public Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }

    public double realPart() {
        return re;
    }

    public double imaginaryPart() {
        return im;
    }

    public Complex plus(Complex c) {
        return new Complex(re + c.re, im + c.im);
    }
    
    ...

    @Override
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof Complex)) return false;
        Complex c = (Complex) o;
        return Double.compare(c.re, re) == 0 && Double.compare(c.im, im) == 0;
    }

    @Override
    public int hashCode() {
        return 31 * Double.hashCode(re) + Double.hashCode(im);
    }

    @Override
    public String toString() {
        return "(" + re + " + " + im + "i)";
    }
}
  • 위의 메서드는 자신을 수정하지 않고 새로운 인스턴스를 만들어서 반환한다.
  • 이처럼 피연산자에 함수를 적용해 그 결과를 반환하지만, 피연산자 자체는 그대로인 프로그래밍 패턴을 함수형 프로그래밍이라고 한다.

불변 객체의 장점

  • 불변 객체는 단순하다. 불변 객체는 생성된 시점의 상태를 파괴될 때 까지 간직한다.
  • 불변 객체는 근본적으로 thread safe하다. 불변객체에 대해 어떤 스레드도 다른 스레드에 대해 영향을 줄 수 없기 떄문이다. 따라서 불변 클래스라면 한번 만든 인스턴스를 최대한 재활용하기를 권장한다.
  • 불변 객체는 방어적 복사가 필요없다 복사를 해도 원본과 같으니 의미가 없다.
  • 불변 객체는 자유롭게 공유할수 있음은 물론 불변 객체끼리는 내부 데이터를 공유할 수 있다.
    • BigInteger 클래스에서 크기를 나타내는 배열인 mag
    • signum 은 부호를 나타내는 int 필드이다
    • mag 은 배열이지만 불변이기 때문에 방어적 복사본을 만들지 않고 원래 객체의 mag 을 사용해도 된다.
      public BigInteger negate() {
      	return new BigInteger(this.mag, -this.signum);
      }
  • 불변 객체는 그 자체로 실패 원자성을 제공한다. 상태가 절대 변하지 않으니 잠깐이라도 불일치 상태에 빠질 가능성이 없다

불변 객체의 단점

  • 값이 다르면 반드시 독립적 개체로 만들어야 한다
  • 예를 들어, 100만 비트자리 BigInteger 에서 비트 하나를 바꿔야 한다고 가정하면 너무 큰 비용을 처리해야 한다.

상속을 막기 위한 방법

상속을 막기 위해서는 클래스를 final로 만드는 방법 외에 생성자를 private 으로 만드는 방법도 있다. 그렇게 한 뒤 정적 팩터리 메서드를 제공하면 된다.

public final class Complex {
    private final double re;
    private final double im;

    private Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }

    public static Complex valueOf(double re, double im) {
        return new Complex(re, im);
    }
	
    ...
}
profile
내 머릿속 지우개

0개의 댓글