자바 언어·플랫폼 용어 사전
언어 기초열거형

enum

상수 묶음이 아니라 인스턴스가 고정된 클래스다. 그 성질이 싱글톤 보장까지 준다.

enum 을 "이름 붙인 상수 묶음" 으로 보면 절반만 이해한 것이다. 실제로는 인스턴스의 개수가 컴파일 시점에 고정된 클래스다.

public enum OrderStatus {
    PENDING("대기") { public boolean cancelable() { return true;  } },
    SHIPPED("배송중") { public boolean cancelable() { return false; } };

    private final String label;
    OrderStatus(String label) { this.label = label; }
    public String label() { return label; }
    public abstract boolean cancelable();
}

각 상수가 객체이므로 필드를 갖고 메서드를 가지며 상수마다 다르게 구현할 수도 있다. if·switch 로 분기하던 것을 각 상수가 스스로 답하게 만들면 분기가 사라진다 — 새 상태를 추가할 때 고칠 곳이 한 군데뿐이라는 이점이 여기서 나온다.

싱글톤이 공짜로 보장된다

인스턴스는 클래스가 로딩될 때 한 번만 만들어진다. 클래스 로딩은 JVM 이 락으로 보호하므로 별도 동기화 없이 단 하나가 보장된다.

여기서 더 중요한 것은 깨뜨릴 방법이 막혀 있다는 점이다.

  • 리플렉션으로 못 만든다enum 생성자 호출은 언어 차원에서 거부된다
  • 직렬화로도 안 늘어난다 — 이름만 쓰고 읽을 때 기존 상수를 찾아 준다. 일반 클래스의 싱글톤은 역직렬화가 새 객체를 만들어 버려서 readResolve 를 직접 써야 한다

그래서 싱글톤이 필요할 때 원소 하나짜리 enum 이 가장 안전한 방법으로 꼽힌다.

== 로 비교할 수 있다

인스턴스가 하나뿐이므로 equals 대신 == 를 쓴다. null 에서 터지지 않고 컴파일 시점에 타입이 다르면 막아 준다는 이점까지 있다.

전용 컬렉션이 빠른 이유

EnumMap·EnumSet 은 해시를 계산하지 않는다. 상수의 선언 순서를 배열 인덱스로 쓰고, EnumSet 은 상수 하나를 비트 한 자리로 표현한다. 원소가 64개 이하면 long 하나로 집합 전체가 표현되고, 합집합·교집합이 비트 연산 한 번에 끝난다. HashMap 과 달리 해시 충돌도 리사이즈도 없다.

switch 에서 누락을 잡아 준다

값의 목록이 고정이므로 컴파일러가 다루지 않은 상수를 지적할 수 있다. 상태를 새로 추가했을 때 처리를 빠뜨린 곳이 컴파일 시점에 드러난다 — 문자열 상수로는 얻을 수 없는 안전망이다.

상속은 못 하지만 인터페이스는 구현한다

모든 enum 은 이미 Enum 을 상속한 상태다. 자바는 단일 상속이므로 다른 클래스를 상속할 수 없다. 대신 인터페이스는 구현할 수 있고, 이것이 확장의 통로다.

interface Discountable { int rate(); }

enum Grade implements Discountable {
    BASIC { public int rate() { return 0;  } },
    VIP   { public int rate() { return 10; } };
}

상수 집합을 확장해야 한다면 enum 을 늘리는 것이 아니라 인터페이스를 두고 여러 enum 이 구현하게 한다. 각 enum 은 여전히 자기 안에서 닫혀 있어 switch 누락 검사를 잃지 않는다.

면접 함정

  • "ordinal() 을 DB 에 저장해도 된다" → 상수 순서를 바꾸거나 중간에 하나 넣으면 기존 데이터의 의미가 전부 어긋난다. 이름을 저장한다.
  • "enum 은 상수라 가볍다" → 클래스이고 객체다. 다만 개수가 고정이라 관리 비용이 없다.
  • "values() 를 마음껏 불러도 된다" → 호출마다 배열을 복사해 돌려준다. 반복문 안에서 부르지 않는다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 기초·타입·객체지향