열거형(enums)은 enumeration의 줄임말로, 어떤 항목을 나열하는 것을 의미한다.
서로 관련된 상수를 편리하게 선언하기 위한 것으로, 여러 상수를 정의할 때 사용한다.
열거형(Enum Type)을 제대로 이해하기 위해서는 열거형이 생겨난 이유를 아는 게 좋다.
1. 문자열
문자열을 사용해서 상품 카테고리와 가격을 입력하면 할인 금액을 계산해주는 클래스를 만들어 보겠다.
public class DiscountService {
public int discount(String category, int price) {
int discountPercent = 0;
if (category.equals("ELECTRONICS")) {
discountPercent = 10;
} else if (category.equals("CLOTHING")) {
discountPercent = 20;
} else if (category.equals("BOOKS")) {
discountPercent = 15;
} else {
System.out.println("category + "는 할인되지 않습니다.");
}
return price * discountPercent / 100;
}
public class StringCategoryMain {
public static void main(String[] args) {
int price = 100_000;
DiscountService discountService = new DiscountService();
int electronics = discountService.discount("ELECTRONICS", price);
System.out.println("Electronics의 할인된 금액: " + electronics); //10000
}
}
이렇게 사용하면 문제가 생긴다.
예를 들어, "ELECTRONICS" 대신에 "electronics"로 잘못 입력한다면 할인되지 않는다.
2. 상수
다음의 코드처럼 "상수를 이용하면 되지 않을까?"라는 생각이 들 것이다.
public class StringCategory {
public static final String ELECTRONICS = "ELECTRONICS";
public static final String CLOTHING = "CLOTHING";
public static final String BOOKS = "BOOKS";
}
public class DiscountService {
public int discount(String category, int price) {
int discountPercent = 0;
if (category.equals(StringCategory.ELECTRONICS)) {
discountPercent = 10;
} else if (category.equals(StringCategory.CLOTHING)) {
discountPercent = 20;
} else if (category.equals(StringCategory.BOOKS)) {
discountPercent = 15;
} else {
System.out.println("category + "는 할인되지 않습니다.");
}
return price * discountPercent / 100;
}
public class StringCategoryMain {
public static void main(String[] args) {
int price = 100_000;
DiscountService discountService = new DiscountService();
int electronics = discountService.discount(StringCategory.ELECTRONICS, price);
System.out.println("Electronics의 할인된 금액: " + electronics); //10000
}
}
문자열 상수를 사용함으로써 코드가 개선되었다.
그럼에도 불구하고, 해결되지 않은 문제가 있다. 꼭 문자열 상수가 아니어도 상관없다는 것이다.
예를 들어, 문자열 상수를 사용하지 않고, 카테고리에 없는 "FURNITURE"라고 입력할 수도 있다.
String
타입이면 입력 가능함자바의 열거형은 "타입에 안전한 열거형(Type-Safe Enum)"이기 때문에 실제 값이 같아도 타입이 다르면 컴파일 에러가 발생한다. 그러므로, 나열한 항목만 사용할 수 있다.
위의 코드를 타입에 안전한 열거형 패턴으로 리팩토링 해보자.
public class ClassCategory {
public static final ClassCategory ELECTRONICS = new ClassCategory();
public static final ClassCategory CLOTHING = new ClassCategory();
public static final ClassCategory BOOKS = new ClassCategory();
private ClassCategory() {}
}
public class DiscountService {
public int discount(ClassCategory category, int price) {
int discountPercent = 0;
if (category == ClassCategory.ELECTRONICS) {
discountPercent = 10;
} else if (category == StringCategory.CLOTHING) {
discountPercent = 20;
} else if (category == StringCategory.BOOKS) {
discountPercent = 15;
} else {
System.out.println("category + "는 할인되지 않습니다.");
}
return price * discountPercent / 100;
}
public class ClassCategoryMain {
public static void main(String[] args) {
int price = 100_000;
DiscountService discountService = new DiscountService();
int electronics = discountService.discount(ClassCategory.ELECTRONICS, price);
System.out.println("Electronics의 할인된 금액: " + electronics); //10000
}
}
직접 열거형 패턴을 사용하여 클래스를 만들 수 있지만, 열거형을 사용할 때마다 매번 직접 구현하려면 많은 코드를 작성해야 한다. 그래서 자바는 타입에 안전한 열거형 패턴을 편리하게 사용할 수 있는 열거형(Enum Type)을 제공한다.
특징
java.lang.Enum
을 상속 받음장점
메서드 | 설명 |
---|---|
values() | 모든 ENUM 상수를 포함하는 배열 반환 |
valueOf(String name) valueOf(Class<T> enumType, String name) | 주어진 이름과 일치하는 ENUM 상수 반환 지정된 enumType에서 주어진 name과 일치하는 ENUM 상수 반환 |
name() | ENUM 상수의 이름을 문자열로 반환 |
ordinal() | ENUM 상수의 선언 순서를 반환(0부터 시작) |
toString() | ENUM 상수의 이름을 문자열로 반환 name()과 유사하지만, toString()은 직접 오버라이드 가능 |
Class<E> getDeclaringClass() | ENUM의 Class 객체 반환 |
위의 코드를 열거형을 사용한 코드로 바꿔보자.
public enum Category {
/**
* 1. 상수이름
* 2. 상수이름(값1, 값2, ...)
**/
ELECTRONICS(10), CLOTHING(20), BOOKS(15);
private final int discountPercent;
Category(int discountPercent) {
this.discountPercent = discountPercent;
}
public int getDiscountPercent() {
return discountPercent;
}
public int discount(int price) {
return price * discountPercent / 100;
}
}
public class CategoryMain {
public static void main(String[] args) {
int price = 100_000;
printDiscount(Category.ELECTRONICS, price);
printDiscount(Category.CLOTHING, price);
printDiscount(Category.BOOKS, price);
}
private static void printDiscount(Category category, int price) {
System.out.println(category.name() + " 카테고리의 할인 금액: " +category.discount(price));
}
}
public enum Category {
ELECTRONICS(10) { int discount(int price) { return price * discountPercent / 100;}},
CLOTHING(20) { int discount(int price) { return price * discountPercent / 100;}},
BOOKS(15) { int discount(int price) { return price * discountPercent / 100;}};
protected final int discountPercent;
Category2(int discountPercent) {
this.discountPercent = discountPercent;
}
public int getDiscountPercent() {
return discountPercent;
}
abstract int discount(int price);
}