brunch

You can make anything
by writing

C.S.Lewis

by 이승현 Mar 19. 2018

클래스와 인터페이스

#15 가변성을 최소화 하자

Effective Java - 클래스와 인터페이스


#15 가변성을 최소화 하자


불변(immutable) 클래스는 자신의 인스턴스가 갖는 값을 변경할 수 없는 클래스입니다.

이 값들은 인스턴스가 생성될 때 설정되고 변하지 않습니다.


이런 불변 클래스들은 가변 클래스에 비해 설계와 구현 및 이용이 더 쉽고, 에러 발생도 적으며 보안이나 이용 측면에서 더 안전합니다.


불변 클래스에 대해서 알아보겠습니다.




불변 클래스


불변 클래스를 만들 때는 다음 다섯 가지 규칙을 따라야 합니다.

객체의 상태를 변경하는 메서드(setter)를 제공하지 않아야 합니다.

상속을 할 수 없습니다. (final class)

모든 필드를 final로 지정합니다.

모든 필드를 private로 지정합니다.

가변 컴포넌트의 직접적인 외부 접근을 막아야 합니다.


#01 Complex class

이 클래스는 복소수(complex number)를 나타냅니다.

복소수가 뭔지 잘 모르겠는데 그냥 복잡한 클래스라고 이해하시면 됩니다.

이 클래스에서는 Object 클래스의 equals, hashcode, toString 메서드를 오버라이드 하고 있으며, 두 double 필드의 접근자 메서드와 4개의 기본 산술 연산 메서드로 구성되어 있습니다.


불변 객체는 생성될 당시의 하나만의 상태만 가질 수 있기 때문에 이 산술 연산에서는 현재의 인스턴스를 변경하지 않고 생성자를 통해 새로운 Complex 인스턴스를 생성하고 반환합니다.

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


기본적인 불변 클래스에 아래와 같이 public static final 상수나 static 팩토리 메서드를 추가하면 클라이언트가 기존 인스턴스를 재사용하여 메모리 소모나 GC 부담을 줄여줄 수 있습니다.


#02 public static final 상수 & static 팩토리 메서드


그리고 불변 클래스는 상속을 할 수 없습니다.

따라서 불변 클래스는 final로 지정할 수 도 있지만 public static 팩토리 메서드를 통해 서브 클래스를 만들지 못하게 할 수 있습니다.


#04 public static 팩토리 메서드


public static 팩토리 메서드를 통해 구현하면 여러 가지 장점이 있는데 이는 아래 링크를 참고해 주시기 바랍니다.





불변 클래스 장단점


1. 멀티 스레드에서 안전하므로 동기화가 필요 없습니다.

불변 객체는 여러 스레드에서 동시에 이용해도 상태가 바뀌지 않습니다.

따라서 별도의 동기화가 필요하지 않습니다.


2. 자유로운 공유가 가능하므로, 그 객체의 방어 복사본(defensive copies)을 만들 필요가 없습니다.

불변 객체는 복사본을 만들어도 원본과 같기 때문에 굳이 만들 필요가 없습니다.

따라서 clone 메서드나 복사 생성자를 구현할 필요가 없습니다.


3. 객체 공유와 더불어 객체의 내부 구조들도 공유할 수 있습니다.

예를 들어, BigInteger 클래스는 내부적으로 부호(signum)와 숫자 값(mag)을 따로 가지고 있습니다.

negate라는 메서드를 통해 부호만 바꿀 수 있는데, 굳이 배열까지 새로 복사할 필요 없이 객체 내부의 구조들을 그대로 이용할 수 있습니다.

#03 BigInteger negate 메서드


4. 다른 객체를 만들 때 이용할 수 있는 훌륭한 컴포넌트입니다.

예를 들어, Map의 key나 Set의 요소로 불변 객체를 이용하면 그 객체가 갖는 값들이 변하지 않으므로 Map이나 Set의 불변 규칙이 깨질 염려가 없습니다.


5. 유일한 단점은, 객체가 가질 수 있는 각 값마다 별개의 객체가 필요합니다.

예를 들어, 100만 개의 비트로 구성된 BigInteger 객체가 있는데 특정 비트만 변경하게 되면 고작 1비트만 변경된 100만 개의 비트로 구성된 새로운 BigInteger 객체가 생성됩니다.

BigInteger의 크기에 비례해서 시간과 공간이 소모되는 일입니다.

이를 해결하기 위해 가변 클래스를 이용할 수 있습니다.

BigInteger 클래스에서는 특정 상황에서 BitSet 가변 클래스를 이용할 수 있고, String 클래스는 StringBuilder를 이용할 수 있습니다.




불변 클래스를 작성하기 위해서 아래와 같은 사항들을 항상 생각하고 있어야 합니다.

인스턴스가 가변적일 필요가 없다면 불변 클래스로 작성해야 합니다.

그리고 필드를 final로 하지 않아야 할 이유가 없다면 모든 필드를 final로 만듭니다.

생성자는 모든 불변 규칙이 확립되어 완벽하게 초기화된 객체를 생성해야 합니다.




매거진의 이전글 클래스와 인터페이스
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari