여러 상수를 선언해야 할 떄, 편리하게 선언할 수 있는 방법을 열거형(enum)이라고 한다.
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;
}
위 처럼 많은 상수를 아래처럼 enum을 사용하면 자동으로 0,1,2,3... 식으로 값까지 부여해 준다.
class Card {
enum Kind { CLOVER, HEART, DIAMOND, SPADE } // 열거형 Kind를 정의
enum Value { TWO, THREE, FOUR } // 열거형 Value를 정의
final Kind kind; // 타입이 Kind임
final Value value;
}
※ 참고로 C에서는 열거형의 값만을 비교하지만 JAVA에서는 해당 열거형의 타입까지 비교하기에 좀 더 안전한 코드를 작성가능하다.
// C
if(Card.Kind.CLOVER == Card.Value.TWO) // 0 == 0으로 true
// JAVA
if(Card.Kind.CLOVER == Card.Value.TWO) // 타입 불일치로 인해 컴파일 에러
열거형을 정의하는 방법은 아래와같다. 그저 괄호 안에 상수의 이름을 나열하기만 하면 된다.
enum 열거형이름 { 상수명1, 상수명2, ... }
동서남북을 상수로 정의한다면 아래와같다.
enum Direction { EAST, SOUTH, WEST, NORTH }
이 열거형에 정의된 상수를 사용하려면 '열거형이름.상수명'처럼 사용한다.
class Unit {
int x, y; // 유닛의 위치
Direction dir; // 열거형 인스턴스 변수를 선언
void init() {
dir = Direction.EAST; // 유닛의 방향을 EAST로 초기화
}
}
열거형 상수간의 비교에는 '=='를 사용할 수 있다. 그만큼 eqauls()보다 빠른 성능을 제공하지만 '<', '>'와 같은 비교연산자를 사용할 수 없지만 compareTo()는 사용가능하다.
※열거형 상수는 하나하나가 객체이기 때문에 <,>가 사용 불가능하다.
if(dir == Direction.EAST) {
x++;
} else if (dir > Direction.WEST) { // 에러. 비교연산자x
...
} else if (dir.compareTo(Direction.WEST) > 0) { // compareTo()가능
...
}
모든 열거형의 조상은 java.lang.Enum이며 아래와 같은 메서드를 제공한다.
이 외에도 컴파일러가 아래 메서드들을 자동으로 추가한다.
static E[] values()
static E valueOf(String name)
enum Direction { EAST, SOUTH, WEST, NORTH };
public class EnumExample {
public static void main(String[] args) {
Direction d1 = Direction.EAST;
Direction d2 = Direction.valueOf("WEST");
Direction d3 = Enum.valueOf(Direction.class, "EAST");
System.out.println("d1 = " + d1);
System.out.println("d2 = " + d2);
System.out.println("d3 = " + d3);
System.out.println("d1 == d2 ? " + (d1 == d2));
System.out.println("d1 == d3 ? " + (d1 == d3));
System.out.println("d1.equals(d3) ? " + d1.equals(d3));
// System.out.println("d2 > d3 ? " + (d1 > d3)); // 에러
System.out.println("d1.compareTo(d3) ? " + (d1.compareTo(d3)));
System.out.println("d1.compareTo(d2) ? " + (d1.compareTo(d2)));
switch(d1) {
case EAST: // Direction.EAST라고 쓸 수 없다.
System.out.println("The Direction is EAST");
break;
case SOUTH:
System.out.println("The Direction is SOUTH");
break;
case WEST:
System.out.println("The Direction is WEST");
break;
case NORTH:
System.out.println("The Direction is NORTH");
break;
default:
System.out.println("Invalid direction.");
}
Direction[] dArr = Direction.values();
for(Direction d : dArr) // for(Direction d : Direction.values())
System.out.printf("%s=%d%n", d.name(), d.ordinal());
}
}
위 예제에서 d.ordinal()로 Direction열거형안의 상수들의 순서를 빼왔다. 따로 상수들에게 값을 주지 않았다면 값 = 위치값 으로 oridnal()만 사용해서 값을 빼 올수 있지만 상수들에게 따로 값을 준 경우라면 순서와 상수의값이 일치하지 않는다.
열거형 상수의 값이 불규칙적인 경우에는 다음과 같이 열거형 상수의 이름 옆에 원하는 값을 괄호()와 함께 적어준다.
enum Direction { EAST(1), SOUTH(5), WEST(-1), NORTH(10) }
이와 함께 저장할 수 있는 인스턴스 변수와 생성자를 추가하는데 이 때 주의해야 할 점은 먼저 열거형 상수를 모두 정의한 다음에 다른 멤버들을 추가해야 한다는 것이다.
enum Direction2 {
EAST(12), SOUTH(24), WEST(31), NORTH(46);
private final int value; // 정수를 저장할 필드(인스턴스 변수)를 추가
Direction2(int value) {
this.value = value;
}
public int getValue() { return value; }
}
열거형의 인스턴스 변수는 반드시 final이어야 한다는 제약은 없지만, value는 열거형 상수의 값을 저장하기 위한 것이므로 final을 붙였다.
Direction2 d = new Direction2(1); //에러. 열거형의 생성자는 외부에서 호출할 수 없다.
열거형의 생성자는 제어자가 private이기 때문에 외부에서 호출불가하다.