본문 바로가기
Java/Java Effective

1. 열거형(Enum)

by mozzi329 2022. 7. 18.
728x90
 

 

     

    봄-여름-가을-겨울

    📌 Goal

    • Enum의 기본적인 개념을 이해하고 설명할 수 있다.
    • Enum이 등장하게 된 배경에 대해서 이해하고, 그 장점에 대해 설명할 수 있다.
    • Enum의 문법 요소를 이해하고 적절하게 사용할 수 있다.

     

    📌 열거형(enum)

    열거형(enum)은 서로 연관된 상수들의 집합을 의미한다.

    상수는 변하지 않는 값을 의미하며 final 키워드를 사용하여 선언할 수 있다. 열거형은 이러한 상수들을 보다 간편하게 관리하기 위해 사용할 수 있는 자바의 문법 요소이다.

     

    JDK 1.5 이전 버전에서는 enum 문법을 지원하지 않는다. 그래서 여러 상수를 정의하기 위해서는 public static final을 통해 전역 변수로 상수를 설정하여 사용했다.

    public static final int SPRING = 1;
    public static final int SUMMER = 2;
    public static final int FALL   = 3;
    public static final int WINTER = 4;
    
    public static final int DJANGO  = 1;
    public static final int SPRING  = 2; // 계절의 SPRING과 중복 발생!
    public static final int NEST    = 3;
    public static final int EXPRESS = 4;
    

    그러나 위의 코드와 같이, 정수 값을 통해 상수를 할당하면 상수명이 중복되는 경우가 종종 발생한다.

     

    이러한 문제는 인터페이스를 사용하여 상수를 구분함으로써 다음과 같이 일차적으로 해결할 수 있다.

    interface Seasons {
    	int SPRING = 1, SUMMER = 2, FALL = 3, WINTER = 4;
    }
    
    interface Frameworks {
    	int DJANGO = 1, SPRING = 2, NEST = 3, EXPRESS = 4;
    }
    

    그러나 이러한 경우 중복상수명의 문제는 피할 수 있지만, 타입의 안전성이라는 새로운 문제가 발생한다.

     

    예를 들면 Seasons.SPRING 의 정수 값 1과 Frameworks.SPRING의 정수 값 2는 상수를 열거하기 위해 임의로 주어졌지만 다음과 같은 코드에 에러가 발생하지 않는다.

    if (Seasons.SPRING == Frameworks.SPRING) {...생략...}
    

    Seasons.SPRING 과 Frameworks.SPRING은 의미적으로 다른 개념임에도 불구하고, 이 둘을 비교하면 에러가 발생하지 않는다. 때문에 타입의 안전성이 떨어질 수 밖에 없다.

     

    이 문제를 해결하기 위해서는 아래와 같이 서로 다른 객체로 만들어주어야 한다.

    class Seasons {
        public static final Seasons SPRING = new Seasons();
        public static final Seasons SUMMER = new Seasons();
        public static final Seasons FALL   = new Seasons();
        public static final Seasons WINTER = new Seasons();
    }
    
    class Frameworks {
        public static final Frameworks DJANGO  = new Frameworks();
        public static final Frameworks SPRING  = new Frameworks();
        public static final Frameworks NEST    = new Frameworks();
        public static final Frameworks EXPRESS = new Frameworks();
    }
    

    위의 코드와 같이 객체를 생성해주면, 상수명의 중복과 타입 안전성 문제를 모두 해결할 수 있다. 그러나 코드가 굉장히 길어지고, 사용자 정의 타입이기 때문에 switch문에 사용할 수 없다는 문제가 발생한다.

     

    이러한 맥락에서, 이같은 문제들을 효율적으로 해결하기 위해 만들어진 것이 바로 enum이다.

    enum Seasons { SPRING, SUMMER, FALL, WINTER }
    enum Frameworks { DJANGO, SPRING, NEST, EXPRESS }
    

    enum을 사용하면 앞서 발생했던 문제들을 모두 효과적으로 해결할 수 있을 뿐 아니라 코드를 단순하고 가독성 좋게 만들 수 있다. 또한 enum으로 정의한 상수는 switch문에서도 사용이 가능하다.

     

    아래의 코드는 enum을 활용해 switch문에 적용해본 예제 코드이다.

    enum Seasons {SPRING, SUMMER, FALL, WINTER}
    
    public class Main {
        public static void main(String[] args) {
            Seasons seasons = Seasons.SPRING;
            switch (seasons) {
                case SPRING:
                    System.out.println("봄");
                    break;
                case SUMMER:
                    System.out.println("여름");
                    break;
                case FALL:
                    System.out.println("가을");
                    break;
                case WINTER:
                    System.out.println("겨울");
                    break;
            }
        }
    }
    
    //출력값
    봄
    

    위의 예제 코드처럼 enum을 사용하면 switch문을 사용할 수 있다는 것을 확인할 수 있다.

     

    정리하자면,

    자바에서 열거형은 여러 상수들을 보다 편리하게 선언하고 관리할 수 있게하며, 상수의 중복을 피하고, 타입에 대한 안정성을 보장한다.

    같은 효과를 낼 수 있는 다른 코드에 비해 훨씬 더 간결하고 가독성이 좋은 코드를 작성할 수 있으며, switch문에서도 작동이 가능하다.

     

    📌 열거형(Enum)의 사용

    ✔️ 열거형(Enum)의 선언

    enum 열거형이름 { 상수명1, 상수명2, 상수명3, ...}
    

     

    사계절을 상수로 정의한다면 다음과 같다. 참고로 상수는 관례적으로 대문자로 작성한다.

    enum Seasons {
    SPRING, //정수값 0 할당
    SUMMER,  //정수값 1 할당
    FALL, //정수값 2 할당
    WINTER //정수값 3 할당
    }
    

    각각의 열거 상수들은 객체이기 때문에, 위의 예제 코드에서 Seasons라는 이름의 열거형 SPRING, SUMMER, FALL, WINTER는 총 네 개의 열거 객체를 포함하고 있다.

     

    마지막으로, 각각의 상수들에는 따로 값을 지정하지 않아도 자동적으로 0부터 시작하는 정수 값이 할당되어 각각의 상수를 가리키게 된다.

     

    ✔️ 열거형(Enum)의 사용

    아래의 예제 코드를 살펴보자.

    enum Seasons { SPRING, SUMMER, FALL, WINTER }
    
    public class EnumExample {
        public static void main(String[] args) {
            System.out.println(Seasons.SPRING); // SPRING
        }
    }
    

    열거형에 선언된 상수에 접근하는 방법은 열거형이름.상수명을 통해 가능하다. 클래스에서 static 변수를 참조하는 것과 동일하다고 할 수 있다.

     

    이제 열거형 상수를 새로운 참조변수에 담아보자.

    enum Level {
      LOW, // 0
      MEDIUM, // 1
      HIGH // 2
    }
    
    public class Main {
      public static void main(String[] args) {
        Level level = Level.MEDIUM;
    
        switch(level) {
          case LOW:
            System.out.println("낮은 레벨");
            break;
          case MEDIUM:
             System.out.println("중간 레벨");
            break;
          case HIGH:
            System.out.println("높은 레벨");
            break;
        }
      }
    }
    
    //출력값
    중간 레벨
    

    위의 예제 코드에서,

    Level이라는 열거형을 만들었고, 그 안에 세 가지의 열거 상수(LOW, MEDIUM, HIGH)를 선언해주었다. 그리고 열거형과 같은 타입의 참조 변수 level에 Level.MEDIUM 값을 할당하고 switch문을 통해 해당 값에 대한 출력 값을 얻을 수 있었다.

     

    이처럼 enum을 사용하면 변경되지 않는 한정적인 데이터들을 효과적으로 불러올 수 있다.

     

    ✔️ 열거형(Enum)에서 사용되는 메서드

    아래 메서드들은 모든 열거형의 조상인 java.lang.Enum에 정의되어있는 것으로, 클래스에서 최상위 클래스 Object에 정의된 메서드들을 사용할 수 있었던 것과 동일하다고 할 수 있다.

     

    리턴 타입 메소드(매개변수) 설명
    String name() 열거 객체가 가지고 있는 문자열을 리턴하며, 리턴되는 문자열은 열거타입을 정의할 때 사용한 상수 이름과 동일하다.
    int ordinal() 열거 객체의 순번(0부터 시작)을 리턴한다.
    int compareTo(비교값) 주어진 매개값과 비교해서 순번 차이를 리턴한다.
    열거 타입 valueOf(String name) 주어진 문자열의 열거 객체를 리턴한다.
    열거 배열 values() 모든 열거 객체들을 배열로 리턴한다.

     

    댓글