자바의 정석[기초편]으로 공부한 내용을 정리한 글입니다.
열거형이란?
- 여러 개의 상수를 정의할 때, 각각 static final로 정의하는 대신 한 번에 묶어서 선언할 수 있는 방식입니다.
- 예: 카드의 무늬(CLOVER, HEART 등)와 숫자(TWO, THREE 등)처럼 고정된 값들의 집합을 만들 때 적합합니다.
기존 방식 (불편하고 길다)
enum 열거형이름 { 상수1, 상수class Card {
static final int CLOVER = 0;
static final int HEART = 1;
static final int DIAMOND = 2;
static final int SPADE = 3;
static final int TWO = 0;
static final int THREE = 1;
static final int FOUR = 2;
final int kind;
final int num;
}
2, 상수3, ... }
- 모든 상수값은 int로 정의됩니다.
- 숫자값이 같더라도 구분이 안 됩니다. (CLOVER == TWO → true)
- 문제점: 어떤 값이 어떤 의미인지 코드만 봐선 알기 어렵고, 타입 안전성(type safety)도 떨어집니다.
enum 사용 (간결하고 타입 안정성 보장)
class Card {
enum Kind { CLOVER, HEART, DIAMOND, SPADE }
enum Value { TWO, THREE, FOUR }
final Kind kind;
final Value value;
}
- enum Kind는 무늬에 대한 열거형, enum Value는 숫자에 대한 열거형입니다.
- 이제 kind는 Kind 타입이고, value는 Value 타입이므로 서로 다른 타입으로 비교됩니다.
장점:
- Card.Kind.CLOVER == Card.Value.TWO → 컴파일 에러 발생
- 타입이 다르므로 비교 자체가 불가능!
- 타입 안전성(type safety) 확보됨
- 코드가 간결하고 읽기 쉬움
열거형의 정의와 사용
열거형(enum) 선언 방식
enum 열거형이름 { 상수1, 상수2, 상수3, ... }
enum Direction { EAST, SOUTH, WEST, NORTH }
- 열거형 상수들은 컴파일 시 static final 상수로 자동 변환됩니다.
- 위 예제에서 Direction.EAST, Direction.SOUTH 등은 각각 고유한 상수입니다.
열거형 사용 예
class Unit {
int x, y;
Direction dir; // 열거형 타입의 인스턴스 변수 선언
void init() {
dir = Direction.EAST;
}
}
- 열거형 변수는 선언 후 열거형이름.상수 형태로 초기화합니다.
열거형 비교
if (dir == Direction.EAST) { ... }
- 열거형은 객체지만 == 비교 가능 (같은 인스턴스를 참조하기 때문)
- .equals()도 가능하지만 == 사용해도 안전함
❗ 주의
if (dir > Direction.WEST) // ❌ 에러 (비교연산자 사용 불가)
- 대신 compareTo()를 사용해야 함
if (dir.compareTo(Direction.WEST) > 0) { ... }
열거형의 조상 - java.lang.Enum
모든 열거형은 Enum<T> 클래스를 상속받음
- 자동으로 메서드 제공됨:
Class<E> getDeclaringClass() | 열거형의 클래스 반환 |
String name() | 열거형 상수의 이름 반환 |
int ordinal() | 정의된 순서 반환 (0부터 시작) |
T valueOf(Class<T> enumType, String name) | 이름으로 상수 얻기 |
values() 메서드
Direction[] dArr = Direction.values();
for(Direction d : dArr)
System.out.printf("%s=%d%n", d.name(), d.ordinal());
- values()는 열거형 상수 전체 배열 반환
- name()은 상수 이름, ordinal()은 순서 반환
예시 출력:
EAST=0
SOUTH=1
WEST=2
NORTH=3
valueOf(String name)
Direction d = Direction.valueOf("WEST");
System.out.println(d); // WEST
System.out.println(Direction.WEST == Direction.valueOf("WEST")); // true
- 문자열 이름으로 상수를 얻을 수 있음
- valueOf("WEST") → Direction.WEST와 같은 상수 객체 반환
요약
전통 방식 | enum 방식 |
int 상수 직접 정의 | enum으로 그룹화된 상수 선언 |
타입 구분 불가능 | 서로 다른 enum 타입은 비교 불가 |
상수 값 중복 가능 | ordinal 자동 지정, 값 중복 없음 |
코드 길고 오류 가능성 있음 | 코드 짧고 명확, 안전성 높음 |
열거형에 멤버 추가하기
기본 전제: ordinal()은 사용 지양
- Enum 클래스에 정의된 ordinal() 메서드는 열거형 상수가 정의된 순서(0부터 시작)를 반환합니다.
- 하지만 이 값은 내부적인 용도로만 쓰이기 때문에, 의미 있는 값으로 사용하지 않는 것이 좋다.
- 예: Direction.EAST.ordinal() → 0, Direction.WEST.ordinal() → 2
의미 있는 값을 넣고 싶을 때는?
ordinal() 대신 우리가 원하는 정수값을 직접 넣고 싶다면, 열거형 상수 옆에 값을 지정하고, 그 값을 저장할 멤버 필드와 생성자를 추가하면 됩니다.
예시
enum Direction {
EAST(1), SOUTH(5), WEST(-1), NORTH(10); // 상수 정의와 동시에 값 지정
private final int value; // 값 저장용 필드
Direction(int value) { // 생성자 정의
this.value = value;
}
public int getValue() { // 외부에서 값 가져오는 메서드
return value;
}
}
포인트 설명:
- EAST(1) 등은 Direction 생성자 호출과 같음 → Direction(1) 호출
- value는 각 상수가 가지는 실제 의미 있는 값
- 외부에서 direction.getValue()로 접근 가능
- value는 final로 선언했기 때문에 상수처럼 한 번만 초기화됨
⚠️ 주의 사항
🔸 열거형 생성자는 외부에서 호출할 수 없다
Direction d = new Direction(1); // ❌ 컴파일 에러
- 이유: enum 생성자는 private이기 때문
- 직접 인스턴스를 생성할 수 없고, 선언된 상수만 사용 가능
enum Direction { ... Direction(int value) { ... } }
→ 생성자는 명시적으로 private을 붙이지 않아도 컴파일러가 자동으로 private로 처리함
마지막 상수 뒤에 ; 붙이기
- enum에 필드나 메서드를 추가하려면, 상수 목록의 끝에 반드시 세미콜론(;)을 붙여야 함
EAST(1), SOUTH(5), WEST(-1), NORTH(10); // 세미콜론 필수!
요약
ordinal() | 상수 정의 순서를 의미. 사용 지양 |
getValue() | 우리가 직접 지정한 의미 있는 정수 반환 |
생성자 | 열거형 내부에서만 사용 가능 (private) |
외부에서 new 사용 | 불가능. 컴파일 에러 |
상수 뒤 ; | 필드나 메서드 정의 시 반드시 필요 |
'Java' 카테고리의 다른 글
표준 애너테이션 (0) | 2025.06.26 |
---|---|
애너테이션 (0) | 2025.06.26 |
와일드 카드, 지네릭 메서드 (0) | 2025.06.26 |
지네릭스 제약네릭스 제약 (0) | 2025.06.25 |
Iterator, HashMap과 지네릭스 (0) | 2025.06.25 |