열거형 - ENUM
문자열과 타입 안정성
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을 상속받았기 때문에 추가로 다른 클래스를 상속받을 수 없다.
- 열거형은 인터페이스를 구현할 수 있다.
- 열거형에 추상메서드를 선언하고, 구현할 수 있다.