주니어 개발자 성장기

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

Java/이펙티브 자바

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

Junpyo Lee 2023. 9. 17. 17:13

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

 

개요

불변 클래스란 소멸될 때까지 인스턴스 내부의 값이 변경되지 않는 클래스다. 쓰는 이유는 간단하다. 쉽고 버그가 발생할 여지가 적어 안전하다. 특히, 멀티 쓰레드 환경에서 안전하게 쓸 수 있다. 캐싱해놓는다면 여러 쓰레드에서 공유하며 사용할 수도 있다.

 

 

 

규칙

 

1. 객체의 상태를 변경하는 메서드를 제공하지 않는다.

setter같이 객체의 상태를 변경하는 메서드를 제공하지 않는다.

 

 

2.클래스를 확장(상속)할 수 없도록 한다.

불변 클래스를 상속하면 가변 클래스로 만들 수 있다. 따라서, final 로 클래스를 선언하거나 private생성자를 만들면 상속을 막아 가변 클래스가 되는 것을 방지할 수 있다.

 

 

3.모든 필드를 final로 선언한다.

변경하지 못하도록 final 키워드를 필드에 선언해준다. (변경 시 컴파일 에러) final은 최대한 많이 쓰는게 좋다. 왜냐하면 코드를 더 견고하게 만들어주고 성능적인 장점도 있기 때문이다.

 

 

4.모든 필드를 private으로 선언한다.(아이템 15, 16)

public으로 필드를 선언하면 내부 표현을 바꾸기 어렵다.

 

 

5. 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록한다.

외부에서 내부의 가변적인 컴포넌트에 접근하는 것을 차단해야한다. 외부에 해당 컴포넌트를 노출해야 한다면 방어적 복사를 해서 내부 컴포넌트를 변경으로부터 안전하게 해야한다.

 

 

 

 

장점

 

 

함수형 프로그래밍에 적합하다.

피연산자들이 변경되지 않아 매번 같은 결과를 보장해준다. 함수형 프로그래밍의 패러다임과 잘 맞는다.

 

 

불변 객체는 단순하다.

객체의 상태가 어떻게 변화할지 경우의 수를 고민할 필요가 없어서 쉽게 사용할 수 있다.

 

 

불변 객체는 근본적으로 스레드 안전하며 따로 동기화할 필요가 없다.

 

불변 객체는 안심하고 공유할 수 있다. (상수, public static final)

불변 객체는 필요에 따라 상수로 설정하고 쓰레드간에 공유하여 사용할 수 있다. (변경의 여지가 없기 때문에)

 

 

불변 객체 끼리는 내부 데이터를 공유할 수 있다.

마찬가지로 변경의 여지가 없기 때문에 내부 데이터를 공유해도 문제가 생길일이 없다.

 

 

객체를 만들 때 불변 객체로 구성하면 이점이 많다.

예를 들어, Collection에 불변 객체가 들어간다면 어떨까? 해당 Collection을 불변으로 만들기가 더욱 쉬워진다.

 

 

실패 원자성을 제공한다.

오류가 발생해도 해당 로직이 실행되기 전 상태를 그대로 유지하고 있기 때문에 원자성이 보장된다.

 

 

 

단점 - 값이 다르다면 반드시 별도의 객체로 만들어야 한다.

다단계 연산을 제공하거나, 가변 동반 클래스를 제공하여 대체할 수 있다. Ex) Stringrepeat메서드 그리고 StringBuilder(가변 동반 클래스)

 

 

 

 

 

 

불변 객체를 만들 때 고려할 점

 

상속을 막을 수 있는 또 다른 방법

private 또는 package-private 생성자 + 정적 팩토리 정적 팩터리를 사용하면 내부적으로 다른 구현체로 바꿔서 제공할 수도 있으며 인스턴스를 캐싱해놓을 수도 있다.

 

 

재정의가 가능한 클래스는 방어적 복사를 사용해야 한다.

불변 클래스가 상속이 가능하다면 자식 클래스들은 가변이 될 수도 있다. 하지만, LSP로 인해 불변 클래스의 자식 클래스들을 불변 클래스인 것처럼 사용할 수 있다. 이런 의도치 않은 상황을 피하려면 방어적 복사를 활용하자.

 

 

모든 “외부에 공개하는” 필드가 final이어야 한다.

계산 비용이 큰 값은 해당 값이 필요로 할 때 (나중에) 계산하여 final이 아닌 핀드에 캐시해서 쓸 수도 있다.