백기선님이 과거에 진행했던 Java 스터디 11주차 스터디 입니다.
enum 정의하는 방법
enum(열거형)은 서로 관련된 상수를 편리하게 선언하기 위한 것으로 여러 상수를 정의할 때 사용하면 유용하다.
열거형을 정의하는 방법은 아래와 같다.
enum 열거형이름 { 상수명1, 상수명2, ... }
열거형에 정의된 상수를 사용하는 방법은 '열거형이름.상수명'이다.
클래스의 static 변수를 참조하는 것과 동일하다.
enum Direction{
EAST, SOUTH, WEST, NORTH
}
class Unit{
int x, y;
Direction dir;
void init(){
this.dir = Direction.WEST; //유닛의 방향을 WEST로 초기화한다.
}
}
열겨형 상수간의 비교에는 ==을 사용할 수 있고, '>', '<'와 같은 비교연산자는 사용할 수 없고 compareTo()는 사용 가능하다.
java.lang.Enum
모든 열거형의 조상은 java.lang.Enum이다.
열겨형 Direction에 정의된 모든 상수를 출력하려면, 다음과 같이 작성한다.
enum Direction{
EAST, SOUTH, WEST, NORTH
}
public class EnumExample {
public static void main(String[] args) {
Direction[] dArr = Direction.values();
for (Direction direction : dArr) {
System.out.printf("%s = %d%n", direction.name(), direction.ordinal());
}
}
}
출력
direction = EAST
direction = SOUTH
direction = WEST
direction = NORTH
values()
values()는 열거형의 모든 상수를 배열에 담아 반환한다. 이 메서드는 모든 열거형이 가지고 있는 것으로 컴파일러가 자동으로 추가해준다.
ordinal()은 모든 열거형의 java.lang.Enum클래스에 정의된 것으로, 열거형 상수가 정의된 순서(0부터 시작)를 정수로 반환한다.
이제 Enum 클래스에서 제공하는 몇 가지 메서드에 대해 알아보자.
Class<E> getDeclaringClass()
열거형의 Class객체를 반환한다.
String name()
열거형 상수의 이름을 문자열로 반환한다.
int ordinal()
열거형 상수가 정의된 순서를 반환한다. (0부터 시작)
T valueOf(Class<T> enumType, String name)
지정된 열거형에서 name과 일치하는 열거형 상수를 반환한다.
(참고로 컴파일러가 valueOf(String name)이라는 메서드도 추가로 생성해준다.)
enum Direction{
EAST, SOUTH, WEST, NORTH
}
public class EnumExample {
public static void main(String[] args) {
Direction[] dArr = Direction.values();
for (Direction d : dArr) {
System.out.println("d.getDeclaringClass() = " + d.getDeclaringClass());
System.out.println("d.name() = " + d.name());
System.out.println("d.ordinal() = " + d.ordinal());
}
System.out.println("Direction.valueOf(\"WEST\") = " + Direction.valueOf("WEST"));
System.out.println("Direction.valueOf(Direction.class,\"EAST\") = " + Direction.valueOf(Direction.class,"EAST"));
}
}
위 코드에서 출력된 결과물은 아래와 같다.
d.getDeclaringClass() = class happysubin.javapractice.javastudy.week11.Direction
d.name() = EAST
d.ordinal() = 0
d.getDeclaringClass() = class happysubin.javapractice.javastudy.week11.Direction
d.name() = SOUTH
d.ordinal() = 1
d.getDeclaringClass() = class happysubin.javapractice.javastudy.week11.Direction
d.name() = WEST
d.ordinal() = 2
d.getDeclaringClass() = class happysubin.javapractice.javastudy.week11.Direction
d.name() = NORTH
d.ordinal() = 3
Direction.valueOf("WEST") = WEST
Direction.valueOf(Direction.class,"EAST") = EAST
enum에 멤버 추가하기
열거형 상수의 값이 불연속적인 경우에는 이때는 다음과 같이 열거형 상수의 이름 옆에 원하는 값을 괄호와 함께 적으면 된다.
enum Direction{
EAST(1), SOUTH(5), WEST(3), NORTH(7);
}
그리고 지정된 값을 저장할 수 있는 인스턴스 변수와 생성자를 새로 추가해 주어야 한다.
이 때 주의할 점은, 먼저 열거형 상수를 모두 정의한 다음에 다른 멤버들을 추가해야한다는 것이다.
그리고 열거형 상수의 마지막에 ';'도 잊지 말아야 한다.
enum Direction{
EAST(1), SOUTH(5), WEST(3), NORTH(7);
private final int value;
Direction(int value) { //접근 제어자 private이 생략되었다.
this.value = value;
}
public int getValue() {
return value;
}
}
열거형 Direction에 새로운 생성자가 추가되었지만, 아래와 같이 열거형의 객체를 생성할 수 없다.
열거형의 생성자는 제어자가 묵시적으로 private이기 때문이다.
Direction d = new Direction(1);
enum에 추상 메서드 추가하기
열거형에 추상 메서드를 선언할 일은 그리 많지 않다고 한다. 가볍게 보고 넘어가자.
enum Transportation{
BUS(100){
@Override
int fare(int distance) {
return distance * BASIC_FARE;
}
},
TRAIN(150){
@Override
int fare(int distance) {
return distance * BASIC_FARE;
}
},
SHIP(100){
@Override
int fare(int distance) {
return distance * BASIC_FARE;
}
},
AIRPLANE(300){
@Override
int fare(int distance) {
return distance * BASIC_FARE;
}
};
protected final int BASIC_FARE; //protected로 해야 각 상수에서 접근가능하다.
Transportation(int basicFare) {
this.BASIC_FARE = basicFare;
}
public int getBASIC_FARE() {
return BASIC_FARE;
}
abstract int fare(int distance); //거리에 따라 계산할 추상 메서드
}
public class EnumExample2 {
public static void main(String[] args) {
System.out.println("Transportation.BUS.fare(100) = " + Transportation.BUS.fare(100));
System.out.println("Transportation.TRAIN.fare(100) = " + Transportation.TRAIN.fare(100));
System.out.println("Transportation.SHIP.fare(100) = " + Transportation.SHIP.fare(100));
System.out.println("Transportation.AIRPLANE.fare(100) = " + Transportation.AIRPLANE.fare(100));
}
}
출력 값은 아래와 같다.
Transportation.BUS.fare(100) = 10000
Transportation.TRAIN.fare(100) = 15000
Transportation.SHIP.fare(100) = 10000
Transportation.AIRPLANE.fare(100) = 30000
예제에서는 오버라이딩한 fare가 동일하지만 언제든지 다르게 바뀔 수 있으므로, 추상 머세드로 선언한 것이다.
정리하면 enum에서 추상 메서드를 선언하면 각 enum 상수가 이 추상 메서드를 오버라이딩해 구현해야 한다.
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 NOTRH = new Direction("NORTH");
private String name;
public Direction(String name) {
this.name = name;
}
}
Direction클래스의 static 상수 EAST, SOUTH, WEST, NORTH의 값은 객체의 주소이고,
이 값은 바뀌지 않는 값이므로 == 비교가 가능한 것이다
EnumSet
우선 EnumSet의 상속 구조는 아래 이미지와 같다.
EnumSet을 사용할 경우 몇 가지 중요한 사항을 고려해야 한다.
- enum 값만 포함할 수 있으며 모든 값이 동일한 enum에 속해야 한다.
- Null 값을 추가할 수 없으며, Null 값이 들어오면 NullPointerException 예외를 던진다.
- 스레드 세이프가 아니므로 필요할 경우 외부에서 동기화해야 한다.
- enum에 선언된 순서에 따라 저장된다.
- 만약 iterator가 돌면서 컬렉션이 수정되어도,
iterator에서 작동하는 Fail-Safe 반복기를 사용하므로 ConcurrentModificationException예외가 발생하지 않는다.
비트 벡터를 사용하므로 성능이 매우 우수하다고 한다.
EnumSet을 생성하는 방법은 아래와 같다.
EnumSet.allOf(Color.class);
EnumSet.noneOf(Color.class);
아래와 같은 다양한 메서드가 존재한다.
EnumSet<Color> set = EnumSet.noneOf(Color.class);
set.add(Color.RED);
set.add(Color.YELLOW)
set.contains(Color.RED);
set.forEach(System.out::println);
set.remove(Color.RED);
이상으로 포스팅을 마칩니다. 감사합니다!
참고자료
자바의정석 (저자 : 남궁성. 지네릭스, 열거형, 애너테이션)
https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html
댓글