[ODOP:04]Java 함수형 프로그래밍 - 04. 불변성

벤 바이디히 의 'A Functional Approach to Java' 를 읽고 정리 한 내용입니다 이 포스트에서는 불변성에 대해 설명하고 있습니다.

💡
3 장은 딱히 정리 할 내용이 없어보이기에 패스 했습니다

04.불변성

불변성의 의미

DDD 에서는 영속화 대상인 Entity 이외에도 Value Object 라는 개념이 있다

한글로 번역 하면 값 객체 라는 의미 인데, 함수형 에서도 같은 의미로 불변성(Immutable)을 말한다

우리는 코드를 작성 할 때 아래 처럼 작성 하곤 한다

int a = 15;
a = 0;

System.out.println(a);

위 코드에서 값은 변경 될 수 있을까?

정답을 바로 이야기 하자면 위 코드는 값이 변경 된게 아니다

값은 15로, 변하지 않는다.

다만, 변수가 담고있는 값이 다른 값으로 대입 될 뿐이다

불변성의 장점

위에서 봤듯 불변성은 변하지 않는다 는 특성을 가지고 있다

따라서 여러 장점을 가지고 있다

1. 예측 가능성

불변성은 말 그대로의 변경되지 않음 을 보장하기 때문에 어떠한 출력이 나올 지 예측 할 수 있다

위의 예제 에서 우리는 응답이 0 이 나올 것이라고 예측 할 수 있지 않은가?

"에헤이, 저건 너무 쉬운 코드니까 예시 로서는 적합하지 않지~"
int a = b;
a = b;

System.out.println(a);

b 는 무엇일까?

2. 유효성

불변성을 가진 값은 단 한 번의 검증만을 수행 한다는 것을 짐작 할 수 있다

책에서는 이렇게 표현한다

단 한 번의 검증만 필요하며 유효(또는 무효) 상태로 유지 됩니다.

위에서 말했듯, 값은 변경되지 않는다.

따라서 초기화 당시 값에 문제가 없다면 유효 한 것이고, 문제가 있다면 무효 하다는 의미이다

3. 숨겨진 사이드 이펙트 없음

불변성이 갖는 장점은 많다

Entity 는 값 자체가 변경이 가능하다

하지만 불변성을 가진 객체는 변경될 수 없다. 대입 될 뿐이다

값이 만들어질 때 유효성에 대한 검증이 이루어지고 값은 완전하며 변경되지 않는다.

4. 스레드 안전성

멀티 스레드 환경은 매우 어렵다

크리티컬 섹션의 계속된 값의 변경을 성공적으로 이루기 위해 여러 기법을 사용한다

하지만 불변성을 가진 값은, 변경되지 않는다

따라서 크리티컬 섹션의 영향을 받지 않는다.

5. 캐시 가능성 및 최적화

이것은 정상적으로 짜놓은 상태를 가정 한 내용이다.

만일 개발자가 여러 기발한 방법으로 멱등성을 보장하지 않는다면 문제가 발생한다

하지만 멱등성을 지킨다면, 언제나 같은 요청에는 같은 응답을 내리니 손쉽게 캐시 적용이 가능하다

래퍼 타입

String

Java 에서 불변성이 적용 된 가장 대표적인 예시가 바로 String class 이다

해당 클래스는 선언 방식에 따라 저장 되는 메모리 공간이 달라진다

하지만 그럼에도 불구하고 언제나 String 은 불변하다

이 처럼 불변한 객체는 Java 에서 여럿 지원 하는데, 아래의 클래스도 마찬가지 이다

BigDecimal

수학적 목적을 위해 사용하는 BigDecimal 이라는 클래스 이다

10 진수로 데이터를 저장하는 특성을 가지고 있는데, 이 때문에 부동소수점 연산에 대한 사이드 이펙트가 없다

책 에서는 말 한다

String 과 마찬가지로 왜 코드에 불변성이라는 부담을 가져와야 할까요? 그 이유는 더 넓은 범위와 높은 정밀도로 사이드 이펙트가 없는 계산을 수행할 수 있기 때문입니다.

여기서 말하는 사이드 이펙트가 없는 계산연산 이 문제 없이 수행 할 수 있는게 불변성 때문이다 라고 오해할 수 있기 때문에 적절한 표현은 아니라고 생각한다. 혹여나 오해하지 말길 바란다.

Date and Time api (JSR-310)

불변성 키워드

enum

Enum 은 불변적 특징을 가지는 클래스 이다.

코드 상에서는 ordinal 값으로 분류 되어 최적화가 이루어 진다

개발자가 지정 한 특정한 네이밍을 JVM 이 모두 ordinal 값으로 치환 하는데 이 기법은 성능을 높이기 위해 사용된다.

불변한 특징을 가지고 있기 때문에 JVM 이 치환이 가능 한 것.

TMI

ordinal 치환 기법은 switch-case 문에서 최적화가 이루어진다.
여기서 Java 17 에서 추가 된 패턴 최적화(검색 키워드: switch-case pattern matching optimization in java) 가 적용 되었을 때의 실험은 해보지 않았다.
만일 실험을 해본다면 나도 알려주길 바란다.

final

자세한 설명은 생략한다.

record

평소 사용 할 때, 이런 궁금증이 생길 수 있다.

아니 왜 Setter 가 없음?

record 는 위에서 설명 된 불변성의 특징을 모두 가지고 있는 키워드 이다.

jdk14 에서 추가 되었는데, 기본적으로 우리가 생각하는 dto 들이 바로 이 record 에 해당 된다

하지만 정확히는 dto 를 위한 키워드가 아니다. 위에서 설명 했듯, 불변성을 위한 객체이다

그렇기 때문에 setter 가 존재해서는 안되며, 값으로서 다루어져야 한다

질문

트래픽이 높은 서비스라면 어떤 문제가 생길 수 있을까?

불변성을 만족하는 객체는 언제나 새로 만들어 져야 한다.

하지만 여러 요청을 처리하는 서버 에서 트래픽이 높아 질 경우 특이한 문제가 발생 할 수 있다

어떤 문제가 발생 할 수 있을까?

불변성을 가진 객체는 값의 변경이 불가능하다

그렇기에 값의 생성이 필연적으로 반복되게 되는데, 값의 생성이 너무 빈번하게 일어나는 경우 GC 가 메모리를 비우는 속도 보다 값의 생성 속도가 더 빨라서 메모리 부족(OOM) 이 발생 할 수 있다.