enum은 java 5 부터 도입된 참조 타입으로, 서로 관련된 상수 집합을 하나의 타입으로 정의해 타입 안전성과 가독성을 보장하는 특별한 클래스이다.
또한 enum은 클래스 인스턴스를 상수로 사용하기 위해 제공되는 특수한 클래스 타입이기도 한데 아래에서는 이러한 enum의 기초 사용법과 작동원리에 대해 알아보고자 한다.
1. Enum 이전의 상수 선언 방법
Java 5 이전의 상수 선언 방법은 다음과 같았다.
////////////////////////////// 상수 클래스 선언 방식 //////////////////////////////
public class Status {
public static final int READY = 0;
public static final int RUNNING = 1;
public static final int DONE = 2;
}
public class TaskProcessor {
private int state;
public TaskProcessor(int initialState) {
this.state = initialState;
}
public void process() {
if (state == Status.READY) { … }
}
}
////////////////////////////// 상수 인터페이스 선언 방식 //////////////////////////////
public interface Status {
int READY = 0;
int RUNNING = 1;
int DONE = 2;
}
public class TaskProcessor implements Status {
public void process() {
if (state == READY) { … }
}
}
위와 같은 방식은 다음과 같은 문제점이 있었다.
- int 값만 보고 의미 파악이 어려움
- 잘못된 정수 입력 (ex: Status.RUNNING + 5)을 컴파일러가 잡아내지 못함
- 여러 상수 그룹 간 이름 충돌 위험이 있음
- 인터페이스 필드는 묵시적으로 public static final이기 때문에 선언만으로 상수가 되긴 함
- 하지만 인터페이스를 구현한 클래스가 많아지면 네임스페이스 관리가 어려워짐 (implements Status를 한 모든 클래스가 READY, RUNNING, DONE라는 이름을 자기 멤버로 갖게 됨)
따라서 위와 같은 문제점을 해결하고자 Java 5 부터 Enum 이 도입이 되었다.
2. Enum의 기본 사용법
public enum Color {
RED, GREEN, BLUE;
}
public class ColorUsageDemo {
public static void main(String[] args) {
Color c = Color.GREEN;
// 1) if-else 문으로 분기
if (c == Color.RED) {
System.out.println("빨간색 선택");
} else if (c == Color.GREEN) {
System.out.println("초록색 선택");
} else if (c == Color.BLUE) {
System.out.println("파란색 선택");
}
// 2) switch 문으로 분기
switch (c) {
case RED:
System.out.println("빨간색 선택");
break;
case GREEN:
System.out.println("초록색 선택");
break;
case BLUE:
System.out.println("파란색 선택");
break;
}
// 3) values() 와 for-each 루프
for (Color col : Color.values()) {
System.out.println(col.name() + " (ordinal=" + col.ordinal() + ")");
}
}
}
- enum 선언만으로 정적 상수 객체(Color.RED)가 생성됨
- values(), valueOf(), name(), ordinal() 와 같은 메소드 사용 가능
values()
- 선언된 모든 상수를 배열로 반환
- 리턴 타입 : YourEnum[] (개발자가 구현한 enum 클래스 배열)
Color[] all = Color.values();
for (Color c : all) {
System.out.println(c);
}
// 출력:
// RED
// GREEN
// BLUE
valueOf(String name)
- 주어진 이름과 일치하는 상수를 반환
- 일치하는 상수가 없으면 IllegalArgumentException 발생
Color c = Color.valueOf("GREEN"); // Color.GREEN
Color x = Color.valueOf("PURPLE"); // 예외!
name()
- 해당 상수의 이름(정의된 대로의 문자열)을 반환
- 리턴 타입 : String
System.out.println(Color.RED.name()); // "RED"
ordinal()
- 해당 상수가 선언된 순서(0부터 시작)를 정수로 반환
- 상수 간 순서가 바뀌면 값도 바뀜
System.out.println(Color.RED.ordinal()); // 0
System.out.println(Color.GREEN.ordinal()); // 1
System.out.println(Color.BLUE.ordinal()); // 2
3. Enum의 작동 방식 및 원리
[ 컴파일러가 자동으로 인스턴스화 ]
enum은 primitive type이 아니라 reference type 이다. 그 얘기는 다시 말해서 enum은 인스턴스화 과정이 있고, 인스턴스화 된 객체가 JVM의 heap 영역에 저장된다는 말이다. 하지만 개발자는 단순히 public enum 으로 객체를 선언하고 내부에 문자열 상수값만 지정하면 enum을 사용할 수 있다. 이러한 방법이 가능한 이유는 컴파일러가 자동으로 enum을 인스턴스화 하기 때문이다.
public enum Color {
RED, GREEN, BLUE;
}
위에서 선언한 Color enum은 컴파일 과정을 거치면 다음과 같은 코드가 된다.
public final class Color extends Enum<Color> {
public static final Color RED;
public static final Color GREEN;
public static final Color BLUE;
private static final Color[] $VALUES;
static {
RED = new Color("RED", 0);
GREEN = new Color("GREEN", 1);
BLUE = new Color("BLUE", 2);
$VALUES = new Color[] { RED, GREEN, BLUE };
}
private Color(String name, int ordinal) {
super(name, ordinal);
}
public static Color[] values() {
return $VALUES.clone();
}
public static Color valueOf(String name) {
return (Color) Enum.valueOf(Color.class, name);
}
}
컴파일러는 enum으로 선언된 객체에 자동으로 초기화 과정을 생성하여 new Color(…) 을 통해 하나의 인스턴스를 생성한다. 이렇게 생성된 인스턴스는 JVM의 heap 영역에 저장되고 Color 클래스 안에서 public static final 필드 값이 된다.
[ Java.lang.Enum 상속 구조 ]
모든 enum은 추상 클래스인 Enum<E>를 상속한다.
Enum 클래스 안에 있는 기본 생성자 메소드 protected Enum(String name, int ordinal) 을 통해 인스턴스화가 진행된다.
이러한 java.lang.Enum 은 name(), ordinal(), compareTo(), toString() 와 같은 메소드를 제공한다.
[ 싱글톤 객체 ]
Enum은 싱글톤 객체를 보장받기 때문에 == 연산자로 비교가 가능하다. Enum이 싱글턴인 이유는 클래스 초기화 시 인스턴스가 한 번만 생성되기 때문이다. 또한 Enum의 생성자는 private 접근자이기 때문에 개발자가 임의로 생성자를 호출할 수 없다.
4. Enum 사용 예시
// API 응답 코드 및 메시지 관리
public enum ResultCode {
SUCCESS("200", "성공"),
NO_USER("404", "존재하지 않는 사용자입니다.");
private final String code;
private final String message;
ResultCode(String code, String message) {
this.code = code;
this.message = message;
}
public String getCode() {
return this.code;
}
public String getMessage() {
return this.message;
}
}
// enum은 반드시 하나의 필드만을 선언할 필요가 없으며
// 여러 개의 필드를 선언하고 그에 맞춰 생성자를 정의할 수 있다.
// JPA 엔티티 상태 표현
@Entity
public class Order {
@Id @GeneratedValue
private Long id;
@Enumerated(EnumType.STRING)
private OrderStatus status;
// ...
}
public enum OrderStatus {
CREATED, PAID, SHIPPED, CANCELLED;
}
// 권한 관리
public enum Role {
GUEST(List.of("READ")),
USER( List.of("READ", "WRITE")),
ADMIN(List.of("READ", "WRITE", "DELETE"));
private final List<String> permissions;
Role(List<String> permissions) { this.permissions = permissions; }
public List<String> getPermissions() {
return Collections.unmodifiableList(permissions);
}
}
'Java' 카테고리의 다른 글
함수형 프로그래밍 (2) | 2025.06.06 |
---|---|
Java Collection Framework (0) | 2025.05.25 |
Singleton 패턴 (0) | 2024.07.16 |
SOLID 원칙 (0) | 2024.07.13 |
추상 클래스와 인터페이스 (0) | 2024.07.13 |