본문 바로가기
Java

Java 중급 1 - 열거형 Enum

by KongJiHoon 2025. 2. 24.

1. 문자열과 타입 안정성1

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 0;
        }

        return price * discountPercent / 100;
    }
}

 

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);
    }

}

 

실행결과

dIAMOND할인X
BASIC등급의 할인 금액: 1000
GOLD등급의 할인 금액: 2000
DIAMOND등급의 할인 금액: 0

 

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

  • 값의 제한 부족: String 으로 상태나 카테고리를 표현하면, 잘못된 문자열을 실수로 입력할 가능성이 있다. 예를
    들어, "Monday", "Tuesday" 등을 나타내는 데 String 을 사용한다면, 오타("Munday")나 잘못된 값
    ("Funday")이 입력될 위험이 있다.
  • 컴파일 시 오류 감지 불가: 이러한 잘못된 값은 컴파일 시에는 감지되지 않고, 런타임에서만 문제가 발견되기 때문
    에 디버깅이 어려워질 수 있다.

 

2. 문자열과 타입 안정성2

package enumeration.ex1;

public class StringGrade {

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

}

 

package enumeration.ex1;

public class DiscountService {

    public int discount(String grade, int price) {

        int discountPercent = 0;

        if (grade.equals(StringGrade.BASIC)) {
            discountPercent = 10;
        } else if (grade.equals(StringGrade.GOLD)) {
            discountPercent = 20;
        } else if (grade.equals(StringGrade.DIAMOND)) {
            discountPercent = 30;
        } else {
            System.out.println(grade + "할인X");
            return 0;
        }

        return price * discountPercent / 100;
    }
}

 

package enumeration.ex1;

import enumeration.ex1.DiscountService;

public class StringGradeEx1_1 {

    public static void main(String[] args) {

        int price = 10000;

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


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

}

 

실행결과

BASIC등급의 할인 금액: 1000
GOLD등급의 할인 금액: 2000
DIAMOND등급의 할인 금액: 3000
  • 첫번째로 구현한 코드와 달리 문자열 상수를 사용하여 전체적으로 코드가 명확해졌다.
  • 하지만 문자열 상수를 사용해도, String 타입은 어떤 문자열이든 입력할 수 있기 때문에 근본적인 문제는 해결할 수 없다
int diamond = discountService.discount("DIAMOND", price);
  • 다음과 같이 직접 문자열을 사용할 수 있기 때문에 근본적인 문제는 해결 x

 

3. 타입 안전 열거형 패턴

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() {}

}

 

  • 각각을 상수로 선언하기 위해 static , final 을 사용한다.
  • static 을 사용해서 상수를 메서드 영역에 선언한다.
  • final 을 사용해서 인스턴스(참조값)를 변경할 수 없게 한다.
  • 위 방식을 이용하면 값을 변경할 수 없기 때문에 위에서 다루었던 문제들을 해결할 수 있다.
  • 또한 외부에서 임의로 ClassGrade의 인스턴스를 생성할 수 없도록 private 생성자를 만들어준다.
  • private 생성자 덕분에 ClassGrade 의 인스턴스를 생성하는 것은 ClassGrade 클래스 내부에서만 할 수
    있다. 앞서 우리가 정의한 상수들은 ClassGrade 클래스 내부에서 ClassGrade 객체를 생성한다.
    이제 ClassGrade 인스턴스를 사용할 때는 ClassGrade 내부에 정의한 상수를 사용해야 한다. 그렇지 않으
    면 컴파일 오류가 발생한다.
    쉽게 이야기해서 ClassGrade 타입에 값을 전달할 때는 우리가 앞서 열거한 BASIC , GOLD , DIAMOND 상수
    만 사용할 수 있다

 

제한된 인스턴스 생성: 클래스는 사전에 정의된 몇 개의 인스턴스만 생성하고, 외부에서는 이 인스턴스들만 사용
할 수 있도록 한다. 이를 통해 미리 정의된 값들만 사용하도록 보장한다.

 

타입 안전성: 이 패턴을 사용하면, 잘못된 값이 할당되거나 사용되는 것을 컴파일 시점에 방지할 수 있다. 예를 들
어, 특정 메서드가 특정 열거형 타입의 값을 요구한다면, 오직 그 타입의 인스턴스만 전달할 수 있다. 여기서는 메
서드의 매개변수로 ClassGrade 를 사용하는 경우, 앞서 열거한 BASIC , GOLD , DIAMOND 만 사용할 수 있다.

 

 

4. 열거형 Enum

**자바는 타입 안전 열거형 패턴"(Type-Safe Enum Pattern)을 매우 편리하게 사용할 수 있는 열거형(Enum Type)을
제공한다.

 

영어인 enum 은 enumeration 의 줄임말인데, 번역하면 열거라는 뜻이고, 어떤 항목을 나열하는 것을 뜻한다.
"Enumeration"은 일련의 명명된 상수들의 집합을 정의하는 것을 의미하며, 프로그래밍에서는 이러한 상수들을 사용하
여 코드 내에서 미리 정의된 값들의 집합을 나타낸다.

 

package enumeration.ex3;



public enum Grade {

    BASIC,
    GOLD,
    DIAMOND
}

 

  • Enum 클래스의 코드는 위에서 final static으로 정의한 클래스와 거의 비슷하다.
  • 외부에서 임의로 생성 x

 

package enumeration.ex3;

public class EnumRefMain {

    public static void main(String[] args) {
        System.out.println("class BASIC = " + Grade.BASIC.getClass());
        System.out.println("class BASIC = " + Grade.GOLD.getClass());
        System.out.println("class BASIC = " + Grade.DIAMOND.getClass());


        System.out.println("ref BASIC = " + refValue(Grade.BASIC));
        System.out.println("ref GOLD = " + refValue(Grade.GOLD));
        System.out.println("ref DIAMOND = " + refValue(Grade.DIAMOND));



    }

    private static String refValue(Object grade) {
        return Integer.toHexString(System.identityHashCode(grade));
    }
}

 

실행결과

class BASIC = class enumeration.ex3.Grade
class BASIC = class enumeration.ex3.Grade
class BASIC = class enumeration.ex3.Grade
ref BASIC = 1b0375b3 // 참조값 -> 각각의 참조값은 다 다르다
ref GOLD = 2f7c7260
ref DIAMOND = 2d209079
  • System.identityHashCode(grade) : 자바가 관리하는 객체의 참조값을 숫자로 반환한다.
  • Integer.toHexString() : 숫자를 16진수로 변환, 우리가 일반적으로 확인하는 참조값은 16진수

** Enum을 활용한 예제코드

package enumeration.ex3;




public class DiscountService {

    public int discount(Grade grade, int price) {

        int discountPercent = 0;

        if (grade == grade.BASIC) {
            discountPercent = 10;
        } else if (grade == grade.GOLD) {
            discountPercent = 20;
        } else if (grade == grade.DIAMOND) {
            discountPercent = 30;
        } else {
            System.out.println(grade + "할인X");

        }

        return price * discountPercent / 100;
    }
}

 

package enumeration.ex3;


import enumeration.ex2.ClassGrade;

public class ClassGradeEx3_1 {

    public static void main(String[] args) {

        int price = 10000;

        DiscountService discountService = new DiscountService();

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


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


    }
}

 

  • 열거형(ENUM)의 장점
    • 타입 안정성 향상: 열거형은 사전에 정의된 상수들로만 구성되므로, 유효하지 않은 값이 입력될 가능성이 없다. 이
      런 경우 컴파일 오류가 발생한다.
    • 간결성 및 일관성: 열거형을 사용하면 코드가 더 간결하고 명확해지며, 데이터의 일관성이 보장된다.
      확장성: 새로운 회원 등급을 타입을 추가하고 싶을 때, ENUM에 새로운 상수를 추가하기만 하면 된다.

 

5. 열거형 주요 메서드

package enumeration.ex3;

import java.util.Arrays;

public class EnumMethodMain {

    // 모든 Enum 반환

    public static void main(String[] args) {
        // 모든 Enum 반환
        Grade[] values = Grade.values();

        System.out.println("values = " + Arrays.toString(values));

        for (Grade value : values) {
            System.out.println("name= " + value.name() + ", ordinal = " + value.ordinal());
        }


        // String -> Enum 변환

        String input = "GOLD";

        Grade gold = Grade.valueOf(input);
        System.out.println("gold = " + gold);

    }
}

 

실행결과

values = [BASIC, GOLD, DIAMOND]
name=BASIC, ordinal=0
name=GOLD, ordinal=1
name=DIAMOND, ordinal=2
gold = GOLD

 

  • ENUM - 주요 메서드
    • values(): 모든 ENUM 상수를 포함하는 배열을 반환한다.
    • valueOf(String name): 주어진 이름과 일치하는 ENUM 상수를 반환한다.
    • name(): ENUM 상수의 이름을 문자열로 반환한다.
    • ordinal(): ENUM 상수의 선언 순서(0부터 시작)를 반환한다.
    • toString(): ENUM 상수의 이름을 문자열로 반환한다. name() 메서드와 유사하지만, toString() 은 직접
      오버라이드 할 수 있다.
  • 주의 ordinal()은 가급적 사용하지 않는 것이 좋다.

 

6. 열거형 리펙토링

package enumeration.ref3.ref2;

public enum Grade {

    BASIC(10),
    GOLD(20),
    DIAMOND(30),
    VIP(40);


    private final int discountPercent;

    Grade(int discountPercent) {
        this.discountPercent = discountPercent;
    }

    public int getDiscountPrice(int price) {
        return price * discountPercent / 100;
    }
}

 

  • discountPercent 필드를 추가하고, 생성자를 통해서 필드에 값을 저장한다.
  • 열거형은 상수로 지정하는 것 외에 일반적인 방법으로 생성이 불가능하다. 따라서 생성자에 접근제어자를 선언할
    수 없게 막혀있다. private 이라고 생각하면 된다.
  • BASIC(10) 과 같이 상수 마지막에 괄호를 열고 생성자에 맞는 인수를 전달하면 적절한 생성자가 호출된다.
    값을 조회하기 위해 getDiscountPercent() 메서드를 추가했다. 열거형도 클래스이므로 메서드를 추가할
    수 있다. 
package enumeration.ref3.ref2;


public class ClassGradeRefMain {

    public static void main(String[] args) {

        int price = 10000;

//
//        printDiscount(Grade.BASIC, price);
//        printDiscount(Grade.GOLD, price);
//        printDiscount(Grade.DIAMOND, price);

        Grade[] grades = Grade.values();

        for (Grade grade : grades) {
            printDiscount(grade, price);
        }
    }

    private static void printDiscount(Grade grade, int price) {
        System.out.println(grade.name() + "등급의 할인 금액: " + grade.getDiscountPrice(price));
    }
}
실행 결과
BASIC 등급의 할인 금액: 1000
GOLD 등급의 할인 금액: 2000
DIAMOND 등급의 할인 금액: 3000

 

'Java' 카테고리의 다른 글

Java 중급 1 - 날짜와 시간(2)  (0) 2025.03.19
Java 중급 1 - 날짜와 시간(1)  (0) 2025.03.16
Java 중급 1 - Wrapper Class  (0) 2025.02.24
Java 중급 1 - String  (0) 2025.02.14
Java 중급 1 - 불변객체  (0) 2025.02.14