Skip to content

Latest commit

 

History

History
84 lines (64 loc) · 4.5 KB

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

File metadata and controls

84 lines (64 loc) · 4.5 KB

3/25 이펙티브 자바

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

불변 클래스

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

불변 클래스를 만드는 5가지 규칙

  • 객체의 상태를 변경하는 메서드를 제공하지 않는다.
  • 클래스를 확장할 수 없도록 한다.
    • final class 혹은 정적 팩터리 사용
  • 모든 필드를 final로 선언한다.
  • 모든 필드를 private으로 선언한다.
  • 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.
    • 클래스에 가변 객체를 참조하는 필드가 하나라도 있다면 클라이언트에서 참조할 수 없도록 한다.
    • 접근자 메서드가 해당 필드를 그대로 반환해서도 안 된다.
    • 생성자, 접근자, readObject 메서드에서 방어적 복사를 수행해야 한다.

불변 클래스의 예시 — 복소수 클래스

private final class Complex {
    private final double re; // real part
    private final double im; // imaginary part
    // ...
    public Complex plus(Complex c) {
        return new Complex(re + c.re, im + c.im);
    }
  • 인스턴스 자신을 수정하는 것이 아닌, 새로운 인스턴스를 만들어 반환하고 있다(함수형 프로그래밍).
    • 함수형 프로그래밍: 피연산자에 함수를 적용해 그 결과를 반환하지만, 피연산자 자체는 그대로인 프로그래밍 패턴
    • 절차형(명령형) 프로그래밍에서는 메서드에서 피연산자인 자신을 수정해 자신의 상태가 변한다.
  • add라는 이름을 사용하지 않고 plus라는 이름을 사용했다. 이처럼 메서드가 객체의 값을 변경하지 않는다는 사실을 메서드명으로 강조할 수 있다.

불변 객체의 장점

  • 생성된 시점의 상태를 파괴될 때까지 그대로 간직한다.
  • 근본적으로 스레드 안전하여 따로 동기화할 필요가 없고, 따라서 안심하고 공유할 수 있다.
    • 방어적 복사도 필요가 없다. 아무리 복사해도 원본과 똑같다.
    • 따라서 clone과 같은 메서드를 제공하지 않는 것이 좋다.
  • 불변 객체끼리는 내부 데이터를 공유할 수 있다.
  • 불변 객체는 그 자체로 실패 원자성(failure atomicity)을 제공한다.
    • 실패 원자성: 호출된 메서드가 실패하더라도 해당 객체는 메서드 호출 전 상태를 유지해야 한다.
    • 상태가 변하지를 않으니 잠깐이라도 불일치에 빠질 가능성이 없다.

불변 객체의 단점

  • 값이 다르면 반드시 독립된 객체로 만들어야 하고, 이는 큰 비용으로 이어질 수 있다.
  • 원하는 객체를 완성하기까지의 단계가 많고, 그 중간 단계에서 만들어진 객체들이 모두 버려진다면 성능 문제가 더 불거진다.
  • 이를 개선하기 위해 가변 동반 클래스가 사용될 수 있다. ex) StringStringBuilder

잘못 설계된 불변 객체를 사용할 때의 주의점

  • 자바의 BigIntegerBigDecimal은 재정의할 수 있도록 잘못 설계되었다.

  • 불변 인스턴스인 경우에만 보안이 유지된다면, 진짜로 해당 클래스의 인스턴스인지 확인해야 한다.

    public static BigInteger safeInstance(BigInteger val) {
        return val.getClass() == BigInteger.class ?
            val : new BigInteger(val.toByteArray());
    }

성능 개선을 위한 예외 규칙

  • 불변 클래스의 규칙: “모든 필드가 final이고 어떤 메서드도 그 객체를 수정할 수 없다.”
  • 성능을 위해 다음과 같이 완화할 수 있다: “어떤 메서드도 객체의 상태 중 외부에 비치는 값을 변경할 수 없다.”
  • 이처럼 완화된 규칙을 적용하여, 계산 비용이 큰 값을 필드에 캐시해놓는 전략이 가능하다.

정리

  • 클래스는 꼭 필요한 경우가 아니라면 불변이어야 한다.
    • 단순한 값 객체는 항상 불변으로 만들고, 성능이 우려되는 복잡한 객체는 가변 동반 클래스를 고려하자.
  • 불변으로 만들 수 없는 클래스라도 변경할 수 있는 부분을 최소화하자.
  • 합당한 이유가 없다면, 모든 필드는 private final이어야 한다.
  • 생성자는 불변식 설정이 모두 완료된, 초기화가 완벽히 끝난 상태의 객체를 생성해야 한다.