class Card {
static final int CLOVEL = 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;
}
if(Card.CLOVER == Card.TWO)
class Card2 {
enum Kind { CLOVER, HEART, DIAMOND, SPADE } // 열거형 Kind를 정의
enum Value { TWO, THREE, FOUR } // 열거형 Value를 정의
final Kind kind; // 타입이 int가 아닌 Kind임에 주의하자.
final Value value;
if(Card2.Kind.CLOVER == Card.Value.Two) {}
// 컴파일 에러. 타입이 달라서 비교 불가.
// 값은 0으로 동일하지만 타입이 불일치한다.
// 이처럼 자바의 열거형은 값과 타입을 모두 체크한다.
}
if(Card2.Kind.CLOVER == Card.Value.Two)
final int MAX_SIZE = 10;
처럼 선언되는데, 이 상수값이 변경되면 그 상수를 참조하는 모든 소스코드를 다시 컴파일해야한다. 왜냐하면 컴파일러는 상수를 사용하는 곳에 직접 그 값을 입력하기 때문이다. enum 열거형이름 {상수명1, 상수명2, ... }
enum Direction {EAST, SOUTH, WEST, NORTH}
class Unit {
int x, y; // 유닛의 위치
Direction dir; // 열거형 인스턴스 변수를 선언
void init() {
dir = Direction.EAST; // 유닛의 방향을 EAST로 초기화
}
}
‘열거형이름. 상수명’
= 클래스의 static 변수를 참조하는 것과 동일하다.
==
와 compareTo()
를 사용할 수 있다.if (dir == Direction.EAST) {
x++;
} else if (dir > Direction.WEST) { //에러. 열거형 상수에 비교연산자 사용 불가
...
} else if (dir.compareTo(Direction.WEST) > 0 { // compareTo() 사용 가능.
compareTo (뺄셈이라고 생각하면 됨)
왼쪽이 큰 경우: 양수 두값이 같은경우 : 0 오른쪽이 큰 경우: 음수
Direction d1 = Direction.EAST; // 열거형타입.상수이름
Direction d2 = Direction.valueOf("WEST");
Direction d3 = Enum.valueOf(Direction.class, "EAST");
di == d2; // true
d1 == d3; // false
// d2 > d3; // 에러
d1.compareTo(d3) // 0
d1.compareTo(d2) // -2
enum Direction { (EAST(1), SOUTH(5), WEST(-1), NORTH(10); }
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;}
}
모든 열거형은 Enum의 자손이며, 아래의 메서드를 상속받는다.
Class<E> getDeclaringClass()
: 열거형의 Class 객체를 반환.String name()
: 열거형 상수의 이름을 문자열로 반환.int ordinal()
: 열거형 상수가 정의된 순서를 반환. (0부터 시작.)T valueOf(Class<T> enumType, String name)
: 지정된 열거형에서 name과 일치하는 열거형 상수를 반환.static E[] values()
static E valueOf(String name)
Direction[] dArr = Direction.values();
for (Direction d : dArr) {
// enum의 모든 항목의 이름, 순서 출력
System.out.printf("%s = %d%n", d.name(), d.ordinal());
}
Direction d = Direction.valueOf("West");
// Direction.West와 동일.
// 열거형 상수 이름을 통해 enum을 조회.
이해를 돕기 위해 열거형의 내부가 어떻게 구현되어있는지 살펴보자.
enum Direction { (EAST, SOUTH, WEST, NORTH }
사실 열거형 상수 하나하나가 Direction 객체다.
위의 문장을 클래스로 정의하면 다음과 같다.
class Direction {
static final Direction EAST = new Direction("EAST");
static final Direction SOUTH = new Direction("SOUTH");
static final Direction WEST = new Direction("WEST");
static final Direction NORTH = new Direction("NORTH");
private String name;
private Direction(String name) {
this.name = name;
}
}
Direction now = Direction.EAST;
- 열거 타입 변수
now
는 스택 영역에 생성된다. 따라서now
에 저장되는 값은Direction.EAST
로 열거 상수가 참조하는 객체의 번지이다.Direction.EAST
와now
는 서로 같은 Direction 객체를 참조하고 있다. 따라서 두 enum을==
으로 비교할 수 있다. (같은 enum인 경우, 같은 주소를 가지고 있을 것이므로)
모든 열거형은 추상 클래스 Enum의 자손이므로, Enum을 흉내내어 MyEnum을 작성하면 다음과 같다.
abstract class MyEnum<T extends MyEnum <T>> implements Comparable<T> {
static int id = 0; // 객체에 붙일 일련번호. 0부터 시작한다.
int ordinal;
String name = "";
public int ordinal() { return ordinal; }
MyEnum(String name) {
this.name = name;
ordinal = id++; // 객체를 생성할 때마다 id값을 증가시킨다.
}
public int comparableTo(T t) {
return ordinal - t.ordinal();
}
}
MyEnum<T>
와 같이 선언하였다면, comparableTo()를 위와 같이 간단하게 작성할 수 없었을 것이다. 타입 T에 ordinal()이 정의되어 있는지 확인할 수 없기 때문이다.MyEnum<T extends <MyEnum<T>>
와 같이 선언한 것이며, 이것은 타입 T가 MyEnum<T>
의 자손이어야 한다는 의미다. 타입 T가 MyEnum의 자손이므로 ordinal()이 정의되어 있는 것은 분명하므로 형변환 없이도 에러가 나지 않는다.class Direction {
static final Direction EAST = new Direction("EAST");
static final Direction SOUTH = new Direction("SOUTH");
static final Direction WEST = new Direction("WEST");
static final Direction NORTH = new Direction("NORTH");
// ...
}
abstract class Direction extends MyEnum {
static final Direction EAST = new Direction("EAST") { // 익명 클래스
Point move(Point p) {/* 내용 생략 */}
};
static final Direction SOUTH = new Direction("SOUTH") {
Point move(Point p) {/* 내용 생략 */}
};
static final Direction WEST = new Direction("WEST") {
Point move(Point p) {/* 내용 생략 */}
};
static final Direction NORTH = new Direction("NORTH") {
Point move(Point p) {/* 내용 생략 */}
};
private String name;
private Direction(String name) {
this.name = name;
}
abstract Point move(Point p);
}
enum Transportation {
BUS(100) {int fare(int distance) { return distance*BASIC_FARE; }},
TRAIN(150) {int fare(int distance) { return distance*BASIC_FARE; }},
SHIP(100) {int fare(int distance) { return distance*BASIC_FARE; }},
AIRPLANE(300) {int fare(int distance) { return distance*BASIC_FARE; }},
protected final int BASIC_FARE; // protected로 해야 각 상수에서 접근 가능
Transportation(int basicFare) { // private Transportation(int basicFare)
BASIC_FARE = basicFare;
}
public int getBasicFare() { return BASIC_FARE; }
abstract int fare(int distance); // 거리에 따른 요금 계산
}
class EnumEx3 {
public static void main(String[] args) {
System.out.println("bus fare =" + Transportation.BUS.fare(100));
}
}