12-2. 열거형

Hyun Jun·2022년 2월 6일
0

자바의 정석

목록 보기
42/52
post-thumbnail
post-custom-banner

열거형 (Enums)

열거형이란?

연관성 있는 상수의 묶음

클래스와 비슷한데, 상수 핸들링에 조금 더 특화된 특별한 클래스라고 보면 됨

JDK 1.5부터 추가됨

C 언어의 열겨형에 비해 발전된 형태로, 열거형의 타입 안전성까지 고려됨.

C 언어에서는 두 열거형의 값만 같으면 타입과 상관 없이 일치하는 것으로 간주하지만, Java는 타입까지 같아야 일치하는 것으로 간주.

열거형 없이 상수를 사용하면, 해당 상수의 값을 바꿀 때마다 모든 소스를 다시 컴파일해야하는 불편함이 있었으나, 열거형을 사용하면 다시 컴파일할 필요가 없음.

 

열거형의 정의와 사용

정의

enum 열거형이름 { 상수명1, 상수명2, 상수명3, ... }
enum Season { SPRING, SUMMER, AUTUMN, WINTER }

 

사용

열거형이름.상수명

Season currentSeason = Season.WINTER;

 

열거형 상수 간의 비교는 == 사용

비교 연산자 <, >는 사용할 수 없고, compareTo() 사용

if (currentSeason.compareTo(Season.SUMMER) > 0) {
    ...
}

switch문의 조건식에 열거형 사용 가능

switch (currentSeason) {
    case SPRING:
        ...
        break;
    case SUMMER:
        ...
        break;
    case AUTUMN:
        ...
        break;
    case WINTER:
        ...
        break;

}

case문에 Season.SPRING처럼 열거형의 이름을 적으면 안되고 상수명만 적어야함

 

모든 열거형의 조상 - java.lang.Enum

Enum 클래스에는 열거형을 다루는데 필요한 메서드가 정의되어 있음

아래는 그 중 일부를 활용한 예시

class EnumExample {
    public static void main(String[] args) {
        Season[] sArr = Season.values();

        for (Season s : sArr) {
                System.out.printf("%s의 값은 %d%n", s.name(), s.ordinal());
        }
    }
}

enum Season { SPRING, SUMMER, AUTUMN, WINTER }

실행 결과)

SPRING의 값은 0
SUMMER의 값은 1
AUTUMN의 값은 2
WINTER의 값은 3
  • static T[] values(): 열거형의 모든 상수를 배열에 담아 반환

  • String name(): 열거형 상수의 이름을 문자열로 반환

  • int ordinal(): 열거형 상수가 정의된 순서를 반환 (0부터 시작)

  • static T valueOf(String name): 열거형에서 문자열(name)과 일치하는 상수 반환

System.out.println(Season.SPRING == Season.valueOf("SPRING")); // true

 

열거형에 멤버 추가하기

열거형 상수는 값을 지정하지 않아도 순서대로 0, 1, 2, 3, ...의 값을 가지는데, 이는 내부적인 용도이므로 가급적 그대로 사용하지 않는게 좋음.

열거형 상수의 값을 직접 지정해주는 경우에는 다음과 같이 멤버들을 추가해줘야함.

enum Season {
    SPRING(3), SUMMER(7), AUTUMN(2), WINTER(4); // 상수 옆에 괄호로 값을 표시

    private final int value; // 값을 저장할 인스턴스 변수 추가
    Season(int value) { this.value = value; } // 생성자 추가

    public int getValue() { return value; }
};

📌 [주의]

  • 열거형의 상수들을 모두 정의한 다음에 다른 멤버를 추가해야함

  • 값을 저장할 인스턴스 변수와 생성자를 추가해야함

  • 생성자는 암묵적으로 private이므로 외부에서 생성자 호출 불가

    그래서 인스턴스 생성은 내부에서만 일어나고, 클래스 최상단의 SPRING(3)과 같은 상수들은 결국 생성자 호출로 만들어진 객체라고 보면 됨.

class SeasonTest {
    public static void main(String[] args) {
        Season s = Season.SRPING; // 상수의 이름으로 생성자를 호출하는 행위. 참조변수 s는 열거형 내부에서 생성된 객체(상수)를 담게 되는 것.
        System.out.println(s.getValue()); // 3
    }
}

 

열거형에 추상메서드 추가하기

열거형에 추상메서드를 선언하면 모든 상수가 이를 구현할 의무가 생김.

enum Transportation {
    // BUS(100), TRAIN(150), SHIP(100), AIRPLANE(300);
    BUS(100) {
        int getFareByDistance(int distance) {
            return (distance - 100) * BASIC_FARE;
        }
    }, TRAIN(150) {
        int getFareByDistance(int distance) {
            return (distance - 300) * BASIC_FARE;
        }
    }, SHIP(100) {
        int getFareByDistance(int distance) {
            return (distance - 400) * BASIC_FARE;
        }
    }, AIRPLANE(300) {
        int getFareByDistance(int distance) {
            return (distance - 500) * BASIC_FARE;
        }
    };

    private final int BASIC_FARE;

    private Transportation(int basicFare) {
        BASIC_FARE = basicFare;
    }

    abstract int getFareByDistance(int distance); // 거리에 따른 요금을 반환하는 추상 메서드
}

상수마다 구현이 달라질 수 있는 메서드는 이렇게 열거형에 추상메서드로 선언해놓고, 상수에서 각자 구현시키면 됨.

 

열거형의 이해

enum Season { SPRING, SUMMER, AUTUMN, WINTER }

위와 같은 열거형에서 각 상수는 Season의 인스턴스임.

그래서 이를 클래스로 표현해보면 다음과 같음.

class Season {
    static final Season SPRING = new Season("SPRING");
    static final Season SUMMER = new Season("SUMMER");
    static final Season AUTUMN = new Season("AUTUMN");
    static final Season WINTER = new Season("WINTER");

    private String name;

    private Season(String name) {
        this.name = name;
    }
}

따라서 각 상수의 값은 인스턴스의 주소값이고, 이는 불변하므로 ==로 비교 연산이 가능한 것.

여기에 추상 메서드가 추가되면

abstract class Season {
    static final Season SPRING = new Season("SPRING") {
        String explainWeather() { ... }
    };
    static final Season SUMMER = new Season("SUMMER") {
        String explainWeather() { ... }
    };
    static final Season AUTUMN = new Season("AUTUMN") {
        String explainWeather() { ... }
    };
    static final Season WINTER = new Season("WINTER") {
        String explainWeather() { ... }
    };

    private String name;

    private Season(String name) {
        this.name = name;
    }

    abstract String explainWeather();
}

상수들(Season의 인스턴스들)은 익명 클래스로서 추상 메서드를 각자 구현해야 됨. (열거형 내부의 추상 메서드를 각 상수가 구현해야 하는 이유)

익명 클래스: 조상 클래스를 상속받는 일회성 클래스. 선언과 인스턴스 생성이 동시에 일어남

 

모든 열거형의 조상인 Enum 클래스를 모방한 MyEnum 클래스

abstract class MyEnum<T extends MyEnum<T>> implements Comparable<T> {
    static int id = 0; // ordinal()로 구할 수 있는 0, 1, 2, 3, ...의 값

    int ordinal;
    String name = "";

    public int ordinal() { return ordinal; }

    MyEnum(String name) {
        this.name = name;
        ordinal = id++; // 객체(열거형 상수)가 생성될 때마다 1씩 증가
    }

    public int compareTo(T t) { // 열거형 상수끼리 비교할 때 비교 연산자 <, > 대신에 사용됨
        return ordinal - t.ordinal();
    }
}

클래스의 제네릭 타입은 extends MyEnum<T>로 상한선을 걸어주지 않으면 compareTo() 메서드의 매개변수인 t의 타입 안전성을 보장할 수 없게 됨.

t에 열거형이 오지 않으면 ordinal() 호출이 안되기 때문

profile
Back-end Engineer 👨‍💻
post-custom-banner

0개의 댓글