Enum이란?
제목에 Enum은 상수 관리를 위한 도구라고 지었습니다. 사실 Enum은 단순한 상수를 관리하는 도구를 넘어서, 타입 안정성과 가독성을 보장하는 방식으로 객체 지향적으로 상수를 관리하는 해결책입니다. 이에 관한 설명을 하기 위해서 Enum이 도입되기 전 상수가 어떻게 정의되었었는지 알아보겠습니다.
과거에는 어떻게 상수를 정의했을까?
1. final 키워드를 이용한 상수
자바에서 상수를 정의하는 가장 전통적인 방법은 final 키워드를 사용하는 것입니다. 이는 변수를 상수로 만들어 한 번 초기화되면 변경되지 않도록 보장하는 방식입니다. 여기에 static을 함께 사용하여 클래스 로드 시 한 번만 메모리에 할당되게 만들어 효율성을 높일 수 있습니다.
public class Constants {
public static final int MAX_VALUE = 100;
public static final String STATUS_ACTIVE = "ACTIVE";
}
하지만 이 방식에는 몇 가지 단점이 있습니다.
- 가독성 문제: public static final이라는 긴 선언이 코드의 가독성을 떨어트릴 수 있습니다.
- 타입 안정성 부족: 서로 다른 상수들이 정수 또는 문자열 같은 기본 데이터 타입으로 정의되어 있을 때, 이들이 잘못 비교되거나 할당되는 경우입니다. 이러한 경우는 컴파일 단계에서 오류로 잡히지 않아서 실수로 인해 버그를 유발할 수 있습니다.
public class StatusConstants {
public static final int ACTIVE = 1;
public static final int INACTIVE = 0;
}
public class ErrorCodeConstants {
public static final int SUCCESS = 1;
public static final int FAILURE = -1;
}
위 코드에서 StatusConstants와 ErrorCodeConstants는 각각 상태와 오류 코드를 나타내기 위한 상수들로 정의되어 있습니다. 하지만 두 클래스에서 각각 상수 1이 중복되어 사용되고 있기 때문에 잘못된 비교가 이루어질 수 있습니다.
int status = StatusConstants.ACTIVE;
int errorCode = ErrorCodeConstants.SUCCESS;
// 서로 다른 의미의 상수를 비교할 수 있지만, 논리적으로 맞지 않음
if (status == errorCode) {
System.out.println("Status is ACTIVE and ErrorCode is SUCCESS");
} else {
System.out.println("Mismatch between status and error code");
}
status는 상태를 나타내고, errorCode는 오류 코드를 나타내기 때문에, 둘을 비교하는 것은 논리적으로 맞지 않지만, 둘 다 같은 정수 1로 정의되어 있기 때문에, 컴파일러는 비교를 허용하고 실제 실행에서는 이 두 상수가 같은 값이라고 인식하게 됩니다.
// 정상적인 할당
int status = StatusConstants.ACTIVE;
// 실수로 status에 errorCode 상수를 할당
status = ErrorCodeConstants.SUCCESS;
// 잘못 비교했지만, 컴파일 에러가 발생하지 않음
if (status == StatusConstants.ACTIVE) {
System.out.println("Status is ACTIVE");
}
status에는 StatusConstants의 상수를 나타내야 하지만, 실수로 ErrorCodeConstants의 상수를 할당했습니다. 두 상수는 논리적으로 다른 역할을 하지만, 같은 타입의 같은 값을 사용하고 있기 때문에, 컴파일러가 이를 잡아내지 못하고 런타임에 논리적인 오류를 발생시킬 수 있습니다.
2. 인터페이스 상수
자바에서는 인터페이스 내부에도 상수를 선언할 수 있습니다. 인터페이스에 선언된 모든 변수는 자동으로 public static final 속성을 가지므로 이를 생략할 수 있어 코드가 좀 더 간결해집니다.
public interface Status {
String ACTIVE = "ACTIVE";
String INACTIVE = "INACTIVE";
}
가독성은 개선되었지만, 타입 안정성 문제가 여전히 남아 있습니다.
3. 클래스를 이용한 상수 정의
final 상수나 인터페이스 상수 대신 독립된 클래스를 정의하여 그 내부에서 상수들을 객체화하는 방법입니다.
public class Status {
public static final Status ACTIVE = new Status("Active");
public static final Status INACTIVE = new Status("Inactive");
private String name;
private Status(String name) {
this.name = name;
}
@Override
public String toString() {
return this.name;
}
}
이 방식은 상수값을 단순한 숫자나 문자열이 아니라 고유한 객체로 관리하여 타입 안정성을 보장합니다. 하지만 상수 선언이 복잡해지고, 매번 객체를 생성해야 해서 가독성이 다시 떨어졌습니다. 또한 switch 문에서 사용할 수가 없습니다. switch 문은 제한된 타입만 허용하는데, 클래스를 이용한 상수는 객체이므로 사용할 수 없습니다.
Enum의 등장과 장점
위에서 나온 상수 정의 방법들의 공통적인 문제는 상수들이 기본형 타입으로 다뤄지면서 타입 안정성이 떨어지고, 코드의 가독성도 완벽하지 않다는 점입니다. Enum은 단순한 상수를 관리하는 도구를 넘어서, 타입 안정성과 가독성을 보장하고, 객체로 다뤄져 객체 지향적으로 상수를 관리합니다.
public enum Status {
ACTIVE, INACTIVE
}
- 타입 안정성: Enum 타입으로 선언된 상수들은 다른 타입과 비교할 수 없습니다. 즉, Status.ACTIVE는 다른 Enum이나 정수값과 비교할 수 없으며, 컴파일 타임에 에러를 발생시켜 줍니다.
- 가독성: 상수들을 하나의 Enum 타입으로 그룹화하여 표현하므로, 코드의 의도가 명확해집니다.
- switch 문에서 사용 가능
public void printStatus(Status status) {
switch (status) {
case ACTIVE:
System.out.println("active");
break;
case INACTIVE:
System.out.println("inactive");
break;
}
}
Enum 기본적인 선언
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
클래스처럼 보이지만, 상수들의 집합의 정의하는 형식입니다. 열거형(Enum)을 정의하면, 각 상수는 Day.MONDAY, Day.TUESDAY처럼 참조할 수 있습니다.
Enum 기본 메서드
Enum은 java.lang.Enum 클래스를 상속받아 몇 가지 유용한 메서드를 자동으로 포함합니다.
- values(): Enum의 모든 상수값을 배열로 반환
- ordinal(): Enum 상수의 선언된 순서를 반환 (0부터 시작)
- name(): Enum 상수의 이름을 문자열로 반환
for (Day day : Day.values()) {
System.out.println(day.name() + " is at index " + day.ordinal());
}
Enum에 필드와 생성자 추가
Enum은 단순한 상수 집합을 넘어서, 필드와 메서드를 가질 수 있습니다. 상수마다 고유한 값을 부여하거나, 관련된 행동을 추가할 수 있습니다.
public enum Day {
MONDAY("Start of workweek"),
TUESDAY("Second day"),
WEDNESDAY("Midweek"),
THURSDAY("Almost there"),
FRIDAY("End of workweek"),
SATURDAY("Weekend"),
SUNDAY("Rest day");
private String description;
// 생성자
Day(String description) {
this.description = description;
}
// 필드에 접근하는 메서드
public String getDescription() {
return this.description;
}
}
상수를 사용할 때마다 고유한 값을 참조할 수 있습니다.
System.out.println(Day.MONDAY.getDescription()); // 출력: Start of workweek
Enum에 메서드 추가
Enum 내에서 상수에 따른 행동을 정의할 수도 있습니다. 이를 통해 상수별로 서로 다른 동작을 할 수 있습니다.
public enum Operation {
PLUS {
@Override
public int apply(int x, int y) {
return x + y;
}
},
MINUS {
@Override
public int apply(int x, int y) {
return x - y;
}
};
public abstract int apply(int x, int y);
}
System.out.println(Operation.PLUS.apply(3, 2)); // 출력: 5
System.out.println(Operation.MINUS.apply(3, 2)); // 출력: 1
'Java' 카테고리의 다른 글
Java 스레드(Thread), 동기화(synchronized), 스레드풀(ThreadPool) 완벽 이해하기 (1) | 2024.10.23 |
---|---|
Java 제네릭(Generic) & 와일드카드 완벽 이해하기 (0) | 2024.10.05 |
Java 멀티스레드 환경에서 Collection 사용: 동기화된 컬렉션과 Concurrent 컬렉션 (1) | 2024.09.26 |
Java 컬렉션 프레임워크 완벽 이해하기 (0) | 2024.09.26 |
Java 날짜 및 시간 포맷 다루기. SimpleDateFormat, DateTimeFormatter, FastDateFormat (1) | 2024.09.24 |