Java

열거형 - ENUM

공부처음하는사람 2024. 11. 5. 17:43

 

문자열과 타입 안정성

Enum Type이 생겨난 이유에 대해 먼저 알아보자.

 

package enumeration.ex0;

public class DiscountService {

    public int discount(String grade, int price) {
        int discountPercent = 0;
        if (grade.equals("BASIC")) {
            discountPercent = 10;
        } else if (grade.equals("GOLD")) {
            discountPercent = 20;
        } else if (grade.equals("DIAMOND")) {
            discountPercent = 30;
        } else {
            System.out.println(grade + ": 할인X");
        }

        return price * discountPercent / 100;
    }
}

 

회원의 등급에 따라 할인율이 다르게 정해져 있다. BASIC은 10%, GOLD는 20%, DIAMOND는 30%로 설정되어 있다.

 

package enumeration.ex0;

public class StringGradeEx0_1 {

    public static void main(String[] args) {
        int price = 10000;

        DiscountService discountService = new DiscountService();
        int basic = discountService.discount("BASIC", price);
        int gold = discountService.discount("GOLD", price);
        int diamond = discountService.discount("DIAMOND", price);

        System.out.println("BASIC 등급 할인금액: " + basic);
        System.out.println("GOLD 등급 할인금액: " + gold);
        System.out.println("DIAMOND 등급 할인금액: " + diamond);

    }
}

 

위와 같은 코드로 각 등급별로 할인율이 얼마나 적용되는지 확인할 수 있는데, 위의 코드엔 여러가지 문제가 발생할 수있다.  

 

1. 타입 안정성 부족

 - 문자열은 오타가 발생하기 쉬워 에러가 발생할 확률이 높다.

2. 데이터 일관성

 - "GOLD", "gold", "Gold"등 다양한 형식으로 문자열을 입력할 수 있는 문제가 생길 수 있다.

 

String 사용 시 타입 안정성 부족 문제

- 위와 같이 오타가 발생할 확률이 굉장히 높다.

- 컴파일 에러시 디버깅이 어려워질 수 있다.

 

위의 문제를 해결하려면 정확한 문자만이  discount() 메서드에 전달되어야 한다.

 

한가지 방법으로 생각해볼만한 건, BASIC, GOLD, DIAMOND를 상수로 사용하는 것이다.

package enumeration.ex1;

public class StringGrade {

    public static final String BASIC = "BASIC";
    public static final String GOLD = "GOLD";
    public static final String DIAMOND = "DIAMOND";
}

 

위와같이 상수로 사용하게되면, 위와 같은 문제를 막을 수 있을 수 있을 것 같지만 막을 수 없다.

discount() 메서드는 String의 값을 매개변수로 받아오기 때문에, 어떤 문자열을 입력해도 자바의 문법 상 문제가 발생하지 않게 된다.

주석을 통해 StringGrade의 상수를 사용하라고 남겨놓을 수 있지만, 그건 방법이 될 수 없을 것이다.

따라서 상수를 사용하는 방법도 큰 도움이 되지 않는다.

 

타입 안전 열거형 패턴 - Type-Safe Enum Pattern

위의 문제를 해결하기 위한 한가지 방법은 타입 안전 열거형 패턴을 사용하는 것이다. Enum은 열거라는 뜻인데, 어떤 항목을

나열하는것을 의미한다. 우리는 BASIC, GOLD, DIAMOND를 나열하고 있고, 이 나열된 항목들만 사용하는 것이 핵심이다.

위의 문제에선 String처럼 아무 문자열을 사용할 수 있는것이 아닌, 우리가 나열한 항목만 사용할 수 있게 된다.

 

package enumeration.ex2;

public class ClassGrade {
    public static final ClassGrade BASIC = new ClassGrade();
    public static final ClassGrade GOLD = new ClassGrade();
    public static final ClassGrade DIAMOND = new ClassGrade();

    private ClassGrade() {
    }
}

 

타입 안전 열거형 패턴을 구현한 코드이다. 먼저 회원 등급을 다루는 클래스를 만들고, 회원 등급별로 상수를 선언했다.

이 때 각각의 상수마다 별도의 인스턴스를 생성하고 대입한다. 그리고 다른 개발자가 임의로 다른 클래스에서

인스턴스를 생성하는것을 방지하기 위해 기본 생성자에 private 접근제한자를 정의했다.

 

package enumeration.ex2;

public class ClassGradeEx2_1 {

    public static void main(String[] args) {
        
        int price = 10000;
        
        DiscountService discountService = new DiscountService();
        
        int basic = discountService.discount(ClassGrade.BASIC, price);
        int gold = discountService.discount(ClassGrade.GOLD, price);
        int diamond = discountService.discount(ClassGrade.DIAMOND, price);

        System.out.println("BASIC 등급 할인금액: " + basic);
        System.out.println("GOLD 등급 할인금액: " + gold);
        System.out.println("DIAMOND 등급 할인금액: " + diamond);
    }
}

 

위와같이 ClassGrade.BASIC 처럼 내가 나열한 상수만 전달 할 수 있다. 다른 개발자가 인스턴스를 생성하지 못하고, 

오직 내가 의도한대로 코드를 작성하게 된다. 이 방법으로 위의 타입 안정성, 데이터 일관성의 문제를 모두 해결할 수 있다.

 

그러나 이 방법은 너무 많은 코드를 작성해야하고, private 생성자를 추가해야하는 등 신경 쓸 일이 많다.

이때 우리는 Enum 클래스를 사용하는 것이다.

 

열거형 - Enum Type

단순하게 말해서 위에 했던 굉장히 불편하고 복잡한 내용들을 굉장히 쉽고 편리하게 사용하게 해줄 수 있는 열거형 클래스이다.

package enumeration.ex3;

public enum Grade {
    BASIC, GOLD, DIAMOND
}

 

위에서 했던 복잡하게 하나하나 인스턴스화 하지 않아도 enum을 사용하면 굉장히 편리하게 열거형 패턴을 사용할 수 있다.

당연히 Enum 클래스는 외부에서 생성이 불가하다. 생성할 경우 컴파일 에러가 발생한다.

열거형의 장점은

1. 타입 안전성 향상

 - 사전에 정의된 상수들로만 구성되므로 실수로 입력된 값이 실행될 일이 없다. 실행된다면 컴파일 에러가 발생

2. 간결성 및 일관성

 - 열거형을 사용하면 코드가 더 간결해지고 명확하며, 데이터 일관성이 보장된다.

3. 확장성

 - 새로운 회원등급을 추가하고 싶다고 한다면, ENUM 클래스에 상수를 추가하기만 하면 된다.

열거형 주요 메서드

- values(): 모든 ENUM 상수를 포함하는 배열을 반환한다.

- valueOf(String anme): 주어진 이름과 일치하는ENUM 상수를 반환한다.

- name(): ENUM 상수의 이름을 문자열로 반환한다.

- ordinal(): ENUM 상수의 선언 순서(0부터 시작)을 반환한다.

- toSTring(): ENUM 상수의 이름을 문자열로 반환한다. name()과 다른점은 toString은 직접 오버라이드 할 수 있다.

 

* ordinal()은 가급적 사용 자제할 것

 - ordinal()의 값은 중간에 상수를 선언하는 위치가 변경되면 전체 상수의 위치가 모두 변경될 수 있기 때문이다.

(위의 내용을 예로 들면 BASIC 다음에 SILVER가 생성될 때, 1부터 등급이 밀려나게 됨)

열거형 정리

- 열거형은 java.lang.Enum을 자동으로 상속받는다.

- 열거형은 이미 java.lang.Enum을 상속받았기 때문에 추가로 다른 클래스를 상속받을 수 없다.

- 열거형은 인터페이스를 구현할 수 있다.

- 열거형에 추상메서드를 선언하고, 구현할 수 있다.