이번 포스팅에서는
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());
}
아래와 같이 AvailableRent
의 BookState 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();
}
위와 같이 코드를 변경하면
이렇게 정상적으로 값이 설정되는걸 볼 수 있다 👍
순환의 굴레에서 벗어나게 해주셔서 감사합니다