enum

enum constant

[Enum](#enume)

enum

enum는 단순한 상수가 아니라 이름이 지정된 클래스 private static final 인스턴스를 지정하는 제한된 클래스 타입이다

enum은 최상위 클래스, 멤버 클래스 또는 지역 클래스로 정의할 수 있으며 abstract, final, sealed, non-sealed 키워드를 사용할 수 없다

다만 다음과 같은 경우 암묵적으로 final 또는 sealed 키워드를 취급한다

또한 enum 클래스는 record 클래스과 동일하게 extends 절을 가질 수 없고, implements 절만 선언할 수 있다

중첩된 이넘 클래스(지역 이넘 클래스 포함)는 모두 암묵적으로 static이 된다

enum definition

이넘 클래스 바디에는 이넘 상수, 생성자, 멤버, 초기화 블록(인스턴스/정적)을 정의할 수 있으며 인터페이스를 구현할 수 있다

모든 요소가 static final로 관리되므로 같은 값은 항상 같은 인스턴스를 참조한다

클래스와 마찬가지로 생성자를 정의하지 않으면 파라미터가 없는 기본 생성자가 자동으로 적용된다

public enum Coin {

    PENNY(1), 
    NICKEL(5), 
    DIME(10), 
    QUARTER(25);

    {
        System.out.println("coin enum class initiating...");
    }

    static {
        System.out.println("coin enum class loading...");
    }
    
    // 생성자는 public, protected 접근 제어자를 가질 수 없으며
    // 자동으로 private 접근 제어자 범위를 가진다
    Coin(int value) { 
        this.value = value; 
    }

    private final int value;
    
    public int value() { 
        return value; 
    }
    
}

아래와 같이 이넘 클래스에 적용된 static 필드에 대해 생성자, 인스턴스 초기화 블록에 대해 접근할 수 없다

primitive 타입의 정적 변수에만 접근할 수 있다

public enum Color {

    RED, GREEN, BLUE;

    static final Map<String,Color> colorMap = new HashMap<>();

    // 생성자에서 참조 타입의 static 멤버 필드에 접근할 수 없다
//    Color() {
//        colorMap.put(toString(), this);
//    }

    static {
        for (Color c : Color.values())
            colorMap.put(c.toString(), c);
    }
}

위의 Color 이넘 클래스의 바이트 코드는 간단히 아래와 같이 변환된다

Color라는 클래스와 각 이넘 상수의 이름을 가진 private static final Color 인스턴스가 생성되고 ENUM$VALUES 필드를 통해 인스턴스들을 관리한다

각 인스턴스에 대한 순서(ordinal)는 자동으로 할당된다

public final class Color extends Enum<Color> {
    public static final Color RED = new Color("RED", 0);
    public static final Color GREEN = new Color("GREEN", 1);
    public static final Color BLUE = new Color("BLUE", 2);

    private static final Color[] ENUM$VALUES = {RED, GREEN, BLUE};
    static final Map<String,Color> colorMap = new HashMap<>();

    static {
        for (Color c : Color.values())
            colorMap.put(c.toString(), c);
    }
    
    private Color(String name, int ordinal) {
        super(name, ordinal);
    }

    public static Color[] values() {
        return ENUM$VALUES.clone();
    }

    public static Color valueOf(String name) {
        return Enum.valueOf(Color.class, name);
    }
}

enum constant

이넘 클래스 바디에 정의되는 이넘 상수는 이넘 클래스의 인스턴스로, 각 이넘 상수에 대해 단 하나의 인스턴스만 생성되어 == 연산자로 비교하는 게 가능하다

이넘 클래스는 이넘 상수 이외에 어떠한 인스턴스를 가질 수 없으며 명시적으로 이넘 클래스에 대한 초기화를 할 수 없다

이넘 상수의 클래스 바디는 enum 클래스에 대한 자식 익명 클래스(final)로 선언되어 익명 클래스의 규칙을 따른다 -> 생성자를 포함할 수 없으며 접근 가능한 인스턴스 메서드를 재정의하는 경우에만 외부에서 접근할 수 있다

아래의 Greeting 이넘 클래스에서 greet 추상 메서드를 선언하고 각 이넘 상수에서 클래스 바디({})에서 구현하고 있다

public enum Greeting {

    KOREAN {
        @Override
        public String greet() {
            return "안녕";
        }
    },

    ENGLISH {
        @Override
        public String greet() {
            return "hello";
        }
    },

    JAPANESE {
        @Override
        public String greet() {
            return "こんにちは";
        }
    };

    abstract public String greet();
}

Enum<E>

모든 enum 타입의 클래스는 자동으로 Enum<E> 추상 클래스를 상속한다

이 추상 클래스에서 제공하는 메서드는 다음과 같다

ordinal()

각 요소의 순서를 반환한다 (0부터 시작)

System.out.println(Color.RED.ordinal());  // 0
System.out.println(Color.GREEN.ordinal()); // 1
System.out.println(Color.BLUE.ordinal()); // 2

name()

열거형의 이름을 문자열로 반환한다

System.out.println(Color.RED.name()); // "RED"

valueOf()

문자열을 enum 요소로 변환한다

Color c = Color.valueOf("BLUE");
System.out.println(c); // BLUE

values()

모든 enum 요소를 배열로 반환한다

for (Color c : Color.values()) {
    System.out.println(c);
}