Enum 순환참조로 인한 null 초기화 트러블 슈팅

은찬·2023년 10월 2일
3

Java, Spring

목록 보기
2/7

Enum null 초기화 트러블 슈팅

이번 포스팅에서는 Enum 을 사용하다보면 마주할 수 있고, 모르고 당한다면 고통받을 수 있는 트러블 상황에 대해서 얘기하고자 한다.


트러블 상황

AvailableRent 코드 (BookState 를 변수로 들고있음)

public class AvailableRent implements State {

	private final BookState bookState = AVAILABLE_RENT;

	@Override
	public BookState getBookState() {
		return bookState;
	}

	@Override
	public void validateIsAbleToReturn() {
		throw BookException.of(ALREADY_AVAILABLE_RENT);
	}
}

BookState enum 코드

public enum BookState {

	AVAILABLE_RENT("대여 가능", new AvailableRent()),
	RENTED("대여중", new Rented()),
	CLEANING("정리중", new Cleaning()),
	LOST("분실", new Lost());

	private final String description;
	private final State state;

	BookState(final String description, final State state) {
		this.description = description;
		this.state = state;
	}

	public String getDescription() {
		return description;
	}

	public State getState() {
		return state;
	}
}

위 코드에서 아래 코드를 실행시키면

public static void main(String[] args) {
		State state = BookState.AVAILABLE_RENT.getState();
		System.out.println(state.getBookState());
	}

아래와 같이 AvailableRentBookState enum 변수에 null 이 초기화 된 것을 볼 수 있다.

분명 AvailableRent 클래스의 코드를 보면 분명히
private final BookState bookState = AVAILABLE_RENT;
이렇게 초기화 하는 과정이 있는데도 null 로 초기화가 된다 😡


뭐가 문제일까…? 🤔

문제는 AvailableRent 클래스가 BookState enum 을 변수로 참조하고 BookState enum 을 생성하는 과정에서 new AvailableRent()AvailableRent 클래스 인스턴스를 생성하며 순환참조를 하기 때문이다.

enum 은 상수 열거형이기 때문에 enum 에 선언된 값들은 어플리케이션 로딩 시점에 초기화 된다.

우리는 new AvailableRent() 를 호출했을 때 BookState 에 초기화된 enum 이 설정되는 그림을 원했지만 enum 을 초기화 하는 과정에서 new AvailableRent() 를 호출하기 때문에 enum 은 초기화 되지 않고, AvailableRent 에 BookState enum 이 null 로 설정되는 것이다.


트러블 슈팅

해결방법은 간단하다 ⚒️

enum 을 초기화 할 때 바로 인스턴스를 생성하지 않는 것이다.

아래 코드처럼 Supplier 함수형 인터페이스를 활용해서 인스턴스를 동적으로 생성하면 된다.

AVAILABLE_RENT("대여 가능", AvailableRent::new),
	RENTED("대여중", Rented::new),
	CLEANING("정리중", Cleaning::new),
	LOST("분실", Lost::new);

	private final String description;
	private final Supplier<State> stateSupplier;

	BookState(final String description, final Supplier<State> stateSupplier) {
		this.description = description;
		this.stateSupplier = stateSupplier;
	}

	public String getDescription() {
		return description;
	}

	public State getState() {
		return stateSupplier.get();
	}

위와 같이 코드를 변경하면

이렇게 정상적으로 값이 설정되는걸 볼 수 있다 👍

profile
`강한` 백엔드 개발자라고 해두겠습니다

1개의 댓글

comment-user-thumbnail
2023년 10월 5일

순환의 굴레에서 벗어나게 해주셔서 감사합니다

답글 달기