열거형(Enum, Enumeration)은 서로 관련이 있는 연관된 상수들의 집합을 정의하는 특수한 데이터 타입이다.
Java 5 이전에는 상수를 정의할 때 final static int 방식을 사용했지만, 이는 타입 안정성 문제나 가독성 저하를 유발했다. Enum을 활용하면 정해진 값 모음 이외의 잘못된 값이 들어오는 것을 컴파일 시점에 방지할 수 있고, 코드의 의미를 명확하게 전달할 수 있다.
기존에 public static final을 이용해 상수를 정의하던 방식을 살펴보자.
public class DayConstant {
public static final int MONDAY = 1;
public static final int TUESDAY = 2;
}
public class MonthConstant {
public static final int JANUARY = 1;
}
이 방식은 두 가지 큰 문제점을 가진다.
DayConstant.MONDAY와 MonthConstant.JANUARY는 둘 다 값이 1이므로 MONDAY == JANUARY 가 참이 되는 논리적 오류가 발생한다.DayConstant 중 하나를 받고 싶어도 타입이 int이기 때문에 정수 999 같은 엉뚱한 값이 전달되어도 컴파일 에러를 잡아내지 못한다.Enum을 선언할 때는 class 대신 enum 키워드를 사용한다.
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
public class EnumTest {
public static void main(String[] args) {
Day today = Day.FRIDAY;
// 타입 안정성 보장: Day 타입에 속한 값만 대입할 수 있다.
if (today == Day.FRIDAY) {
System.out.println("금요일이다!");
}
// switch 문에서의 활용
switch (today) {
case MONDAY -> System.out.println("월요일");
case FRIDAY -> System.out.println("금요일");
default -> System.out.println("평일 또는 주말");
}
}
}
Java의 모든 Enum은 내장 클래스인 java.lang.Enum을 상속받으므로 유용한 기본 메서드들을 사용할 수 있다.
Day today = Day.WEDNESDAY;
// 1. name(): Enum 상수의 이름을 문자열로 반환한다.
String name = today.name(); // "WEDNESDAY"
// 2. ordinal(): Enum 상수가 정의된 순서(0부터 시작)를 반환한다.
int ordinal = today.ordinal(); // 2
// 3. values(): Enum에 정의된 모든 상수를 배열로 반환한다.
Day[] days = Day.values();
for (Day d : days) {
System.out.println(d);
}
// 4. valueOf(String name): 주어진 문자열과 일치하는 Enum 상수를 반환한다.
Day day = Day.valueOf("MONDAY");
주의:
ordinal()은 상수의 선언 순서에 의존하므로 상수의 순서가 바뀌면 예기치 않은 버그가 발생할 수 있다. 실무에서는ordinal()에 의존하는 로직을 작성하지 않는 것이 바람직하다.
Java의 Enum은 단순한 상수의 집합을 넘어 일반 클래스처럼 필드, 생성자, 메서드를 가질 수 있다.
public enum PayType {
CARD("신용카드", 0.0),
ACCOUNT_TRANSFER("계좌이체", 0.005),
MOBILE("휴대폰 결제", 0.02);
// 필드 선언 (불변성을 위해 final 권장)
private final String description;
private final double feeRate;
// 생성자 (Enum 생성자는 기본적으로 private)
PayType(String description, double feeRate) {
this.description = description;
this.feeRate = feeRate;
}
// Getter 메서드
public String getDescription() {
return description;
}
// 수수료 계산 로직 추가
public double calculateFee(int amount) {
return amount * feeRate;
}
}
public class PayTest {
public static void main(String[] args) {
PayType pay = PayType.MOBILE;
System.out.println("결제 수단: " + pay.getDescription()); // 휴대폰 결제
System.out.println("수수료: " + pay.calculateFee(10000)); // 200.0
}
}