본문 바로가기

언어/자바

Enum의 static field 접근

<오늘 회사에서 enum에 대해서 간과하고 있었던 점을 하나 알게되어서 공유함.>

 

enum을 일반 자바 클래스로 구현하려면 어떻게 해야할까?

아마도 각 상수를 static으로 선언하고 생성자를 private으로 하지 않을까 싶다.

 

실제로 enum의 상수들은 static final로 구현된다.

이 기본적인 개념을 간과하고 있었다.

(enum을 너무 생각없이 쓰게된걸까..?)

 

아래 코드는 에러가 발생한다. 왜 발생할까?

package enumTest;

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        System.out.println("hellop");
        System.out.println(Fruit.fruitMap);
    }
}

enum Fruit {
    APPLE("apple"), ORANGE("orange");

    private String name;
    public static Map<String, Fruit> fruitMap = new HashMap<>();

    Fruit(String name) {
        this.name = name;
        addToMap(this);
    }

    private static void addToMap(Fruit fruit) {
        fruitMap.put(fruit.name, fruit);
    }
}

 

에러 메시지는 아래와 같다.

Exception in thread "main" java.lang.ExceptionInInitializerError
	at enumTest.Main.main(Main.java:8)
Caused by: java.lang.NullPointerException
	at enumTest.Fruit.addToMap(Main.java:24)
	at enumTest.Fruit.<init>(Main.java:20)
	at enumTest.Fruit.<clinit>(Main.java:13)
	... 1 more

 

NPE가 발생했다. 왜 발생했을까?

 

Java의 static field 초기화는 textual order이다.

즉, 선언된 순서대로 초기화된다.

 

위쪽에 선언되어 있을 수록 먼저 선언된다.

 

enum의 상수들은 앞서 말했 듯, static이고 enum 문법상 enum class 내부에서 가장 위에 위치한다.

따라서 fruitMap보다 상수들이 먼저 생성되고, 생성자가 fruitMap이 초기화 되기 전에 호출된다.

 

이를 해결하기 위해서는 코드를 이렇게 수정할 수도 있다.

public class Main {
    public static void main(String[] args) {
        System.out.println(Fruit.fruitMap);
    }
}

enum Fruit {
    APPLE("apple"), ORANGE("orange");

    private String name;
    public static Map<String, Fruit> fruitMap = new HashMap<>();

    Fruit(String name) {
        this.name = name;
    }

    private static void addToMap(Fruit fruit) {
        fruitMap.put(fruit.name, fruit);
    }

    static {
        Arrays.stream(values()).forEach(Fruit::addToMap);
    }
}

 

기억하자.

enum의 상수들은 static이다.

java의 상수 필드는 textual order순으로 초기화 된다.