꾸준한 스터디
article thumbnail

불변클래스

불변 클래스 : 한 번 만들어지면(초기화가 끝나면) 인스턴스가 소멸될 때 까지 상태가 바뀌지 않는 것(해당 클래스가 가지고 있는 필드 값)

  • 불변 클래스는 가변 클래스보다 설계하고 구현하고 사용하기 쉬우며, 오류가 생길 여지도 적고 훨씬 안전하다.
  • 불변 클래스를 만드는 다섯가지 규칙
    • 객체의 상태를 변경하는 메서드를 제공하지 않는다.
    • 클래스를 확장할 수 없도록 한다.
    • 모든 필드를 final로 선언한다.
    • 모든 필드를 private로 선언한다.
    • 자신 외에는 내부의 가변 컴포넌트에 접근할 수 없도록 한다.

 

불변 클래스는 가변 클래스보다 설계하고 구현하고 사용하기 쉬우며, 오류가 생길 여지도 적고 훨씬 안전하다.

멀티 스레드 환경에서 스레드 세이프. 값이 변하지 않으니 여러 스레드에서 사용할 때 캐시해서 사용한다.

 

 

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

setter 메서드를 제공하지 않는다. 초기값 세팅 후 상태값 변경할 수 없게

 

클래스를 확장할 수 없도록 한다.

 

불변클래스를 확장했을 때 가변 객체로 잘못만들면 확장한 상위타입 객체로 사용한다고 했을 때 바뀔 수 있는 객체가 된다. 불변클래스를 상속받았는데 불변객체로 사용할 수 없게 된다.
클래스 시그니처에 final 키워드를 사용하면 해당 클래스를 상속할 수 없다.
생성자 접근제어자가 private 이면 해당 클래스를 외부에서 생성할 수 없다.

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

클래스 내부에서 잘못하여 메서드 내부에서 값이 변경될 수 있다.
final 키워드를 사용하여 값을 변경할 수 없게 만든다.

 

모든 필드를 priavte로 선언한다.

이전 챕터에서 나왔듯이 외부 클래스에서 필드명으로 값에 접근하지 못하게 하고 getter 메서드로 상태값을 받게하는게 유연하게 관리가 가능하다. 필드값이 변경되면 가져다 쓴 모든 클래스 변경이 필요하기 때문

 

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

클래스 내에 가변적인 값을 외부에 제공해야 한다면.. 방어적 복사로 가변 컴포넌트에 접근할 수 없게 만들어야 한다.

불변 클래스의 장점과 단점

  • 함수형 프로그래밍에 적합하다. (피연산자에 함수를 적용한 결과를 반환하지만 피연산자가 바뀌지는 않는다.)
  • 불변 객체는 단순하다
  • 불변 객체는 근본적으로 스레드 안전하여 따로 동기화할 필요 없다.
  • 불변 객체는 안심하고 공유할 수 있다. (상수, public static final)
  • 불변 객체 끼리는 내부 데이터를 공유할 수 있다.
  • 객체를 만들 때 불변 객체로 구성하면 이점이 많다.
  • 실패 원자성을 제공한다.
  • 단점) 값이 다르다면 반드시 별도의 객체로 만들어야 한다.
    • "다단계 연산"을 제공하거나, "가변 동반 클래스"를 제공하여 대처할 수 있다.

 

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

함수형 프로그래밍은 불변성을 지향하는 프로그래밍 언어로 변경 가능한 상태를 최대한 제거하고 Side Effect가 없어야 한다.

plus, valueOf, minus 메서드를 함수형 프로그래밍이라고 하는데 인스턴스 자신을 수정하지 않고 새로운 Complex 인스턴스를 만들어 피연산자의 상태가 변하지 않고 동일한 값이 주어짐을 확인할 수 있다.

 

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

여러 스레드에서 한 인스턴스의 값을 사용하는 경우 실행 순서에 따라 여러 스레드의 값이 물리게 되는 경우가 생길 경우 값이 변경되면 다른 결과값이 나올 수 있는데 불변객체는 그럴 일이 없다.

불변 객체는 안심하고 공유할 수 있는 것도 마찬가지 public static final  정적 필드의 값도 여러 곳에서 공용되어 사용할 목적으로 선언한 필드이지만 불변 객체로 선언한다면 다 같은 값을 사용하게 되니 안심하고 사용할 수 있다.

 

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

불변객체에서 사용할 값이 레퍼런스 타입이라면 해당하는 객체도 불변객체여야 한다. 가변 객체이면 부르는 모체가 불변객체라고 할지라도 값이 달라질 수 있기 때문

 

객체를 만들 때 불변 객체로 구성하면 이점이 많다. 값이 바뀌지 않는 구성요소들로 이뤄진 객체라면 그 구조가 아무리 복잡하더라도 불변식을 유지하기 훨씬 수월하기 때문.

실패 원자성을 제공한다. 상태가 절대 변하지 않으니 잠깜이라도 불일치 상태에 빠질 가능성이 없다.

 

다 비슷한 장점

 

 

단점으로 값이 다르면 독립된 객체로 만들어야 하는데 생성 비용이 비싼 객체라면 불변성을 지키기 위해 다른 값을 가진 객체로 매번 생산해야하기 때문이다.

 

불변 클래스 만들 때 고려할 것

  • 상속을 막을 수 있는 또 다른 방법
    • private 또는 package-private 생성자 + 정적 팩터리(valueOf)
    • 확장이 가능하다. 다수의 package-private 구현 클래스를 만들 수 있다.
    • 정적 팩터리를 통해 여러 구현 클래스 중 하나를 활용할 수 있는 유연성을 제공하고 객체 캐싱 기능으로 성능을 향상 시킬수도 있다.
  • 재정의가 가능한 클래스는 방어적인 복사를 사용해야한다.
  • 모든 "외부에 공개하는" 필드가 final이어야 한다.
    • 계산 비용이 큰 값은 해당 값이 필요로 할 때 (나중에) 계산하여 final이 아닌 필드에 캐시해서 쓸 수도 있다.

 

final과 자바 메모리 모델(JMM)

  • JMM
    • 자바 메모리 모델은 JVM의 메모리 구조가 아니다
    • 적법한(legal) 프로그램을 실행 규칙
    • 메모리 모델이 허용하는 범위 내에서 프로그램을 어떻게 실행하든 구현체(JVM)의 자유다. (이 과정에서 실행 순서가 바뀔 수도 있다.)
  • 어떤 인스턴스의 final 변수를 초기화 하기 전까지 해당 인스턴스를 참조하는 모든 쓰레드는 기다려야 한다.(freeze)
  • 인스턴스의 값이 반드시 초기화가 이루어지고 사용되어야 할 값들은 final 키워드를 사용해라

https://aroundck.tistory.com/3422

 

[Java Concurrency] 자바 메모리 모델

[Java Concurrency] 자바 메모리 모델 - 자바 메모리 모델(JMM, Java Memory Model) 의 내부 구조가 어떻게 동작하는지를 이해하고 있다면 상위 개념을 훨씬 효율적으로 쉽게 사용할 수 있을 것이다. 16.1. 자

aroundck.tistory.com

 

java.util.concurrenet 패키지 : 병행(concurrency) 프로그래밍에 유용하게 사용할 수 있는 유틸리티 묶음

  • 병행(Concurrency)와 병렬(Parallelism)의 차이
  • 병행은 여러 작업을 번갈아 가며 실행해 마치 동시에 여러 작업을 동시에 처리하듯 보이지만, 실제로는 한번에 오직 한 작업만 실행한다. CPU가 한개여도 가능하다.
  • 병렬은 여러 작업을 동시에 처리한다. CPU가 여러개 있어야 가능하다.
  • 자바의 concurrent 패키지는 병행 어플리케이션에 유용한 다양한 툴을 제공한다.
    • BlockingQueue, Callable, ConcurrentMap, Executor, ExecutorService, Future...

CountDownLatch : 다른 여러 스레드로 실행하는 여러 오퍼레이션이 마칠 때까지 기다릴 때 사용할 수 있는 유틸리티

  • 초기화할 때 숫자를 입력하고, await() 메서드를 사용해서 숫자가 0이 될 때까지 기다린다.
  • 숫자를 셀 때는 countDown() 메서드를 사용한다.
  • 재사용할 수 있는 인스턴스가 아니다. 숫자를 리셋해서 재사용하려면 CyclicBarrier를 사용해야 한다.
  • 시작 또는 종료 신호로 사용할 수 있다.

 

https://www.inflearn.com/course/%EC%9D%B4%ED%8E%99%ED%8B%B0%EB%B8%8C-%EC%9E%90%EB%B0%94-2/dashboard

profile

꾸준한 스터디

@StudyRecord

포스팅이 유익하셨다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!