Enum이란
: 열거형(enumerated type)이라고 부르며 서로 연관된 상수들의 집합이다.
: 기존에 상수를 사용하면서 발생했던 TypeSafe 문제를 개선하고자 jdk 1.5부터 추가되었다.
Enum 이전의 상수 표현
•
변수를 상수로 사용
public class Main {
public static void main(String[] args) {
int fruitType = 1;
switch (fruitType) {
case 1:
return 'apple';
case 2:
return 'banana';
case 3:
return 'cherry';
}
}
Java
복사
: 상수는 변하지 않는 수를 의미하므로 변수에 고정된 값을 할당하면 그 변수는 상수가 될 수 있다.
: 이 코드엔 문제가 있다. int fruitType = 1;은 직관적이지 않다. 해당 코드 자체로 어떤 역할을 하는 지 이해할 수 없으며, switch 문까지 가야만 그 용도를 이해할 수 있다.
•
final을 이용한 상수
public class Main {
public final static int APPLE = 1;
public final static int BANANA = 2;
public final static int CHERRY = 3;
public static void main(String[] args) {
int fruitType = APPLE;
switch (fruitType) {
case APPLE:
return 'apple';
case BANANA :
return 'banana';
case CHERRY :
return 'cherry';
}
}
}
Java
복사
: 이제 fruitType에 의미가 생겼다. 우리는 fruitType이 사과인 것을 알 수 있다. 하지만 이것또한 문제가 존재한다.
: 과일 뿐만이 아니라 휴대폰 회사도 상수로 사용할 일이 생겼다고 가정해보자. 그럼 우리는 다음과 같은 상수를 또 추가할 것이다.
public final static int SAMSUNG = 1;
public final static int APPLE = 2;
public final static int XIAOMI = 3;
Java
복사
: APPLE이라는 이름을 가지는 상수는 정적 영역에서 두 번 초기화 되므로 컴파일러가 에러를 토해낼 것이다.
: 물론 접두사를 사용할 수 있다. 다음과 코드와 같이 말이다.
public final static int FRUIT_APPLE = 1;
public final static int FRUIT_BANANA = 2;
public final static int FRUIT_CHERRY = 3;
public final static int CELLPHONE_SAMSUNG = 1;
public final static int CELLPHONE_APPLE = 2;
public final static int CELLPHONE_XIAOMI = 3;
Java
복사
: 이렇게 될 경우, 가독성이 좋지 않다. 또한 과일 관련 로직에서 CELLPHONE을 사용해도 컴파일러는 문제를 잡아내지 못한다. 즉 TypeSafe하지 않다.
: FRUIT_APPLE == CELLPHONE_APPLE 이 코드는 True를 내뱉을 것이다.
Enum 정의
enum Cellphone {
SAMSUNG, APPLE, XIAOMI;
}
Java
복사
: enum 키워드를 사용하여 정의한다.
: 열거형 필드의 이름은 상수므로 대문자로 표시한다.
: 0부터 시작하는 정수값이 연속적으로 부여된다.
중요한 부분
•
모든 enum은 클래스를 사용해서 정의된다.
enum Cellphone {
SAMSUNG, APPLE, XIAOMI;
}
Java
복사
class Cellphone {
public static final Cellphone SAMSUMG = new Cellphone();
public static final Cellphone APPLE = new Cellphone();
public static final Cellphone XIAOMI = new Cellphone();
}
Java
복사
•
enum 값을 비교할 때, ==와 compareTo()만 사용 가능하다.
: >, ≥, <, ≤와 같은 비교 연산자는 당연히 안 된다.
: enum은 클래스다. 클래스 간의 비교 연산자가 될리가 없다. 같은 이유로 클래스 간의 비교기 때문에 compareTo()가 되는 것 같다.
java.lang.Enum
•
public static enum[] values();
: 열거형에 정의된 모든 상수를 출력하기 위해서는 values() 메소드를 사용하면 된다. 해당 Enum의 리스트 형태로 반환된다.
•
public static String valueOf(String name);
: 열거형의 속성 값을 출력해준다. 속성 값이 아닌 인자가 들어올 경우, IllegalArgumentExeption을 발생시킨다.
: 이는 정의된 Enum 클래스를 컴파일 시, java.lang.Enum을 상속하게끔 처리하기 때문이다.
: 바이트 코드를 통해 확인해보면 각각 열거한 상수가 static fianl *** 형태로 선언되며 앞서 언급한 static 메서드들인 values()와 valueOf()도 추가됨을 확인할 수 있다.
// class version 55.0 (55)
// access flags 0x4030
// signature Ljava/lang/Enum<LSeason;>;
// declaration: Season extends java.lang.Enum<Season>
final enum Season extends java/lang/Enum {
// compiled from: Main.java
// access flags 0x4019
public final static enum LSeason; SPRING
// access flags 0x4019
public final static enum LSeason; SUMMER
// access flags 0x4019
public final static enum LSeason; FALL
// access flags 0x4019
public final static enum LSeason; WINTER
// access flags 0x101A
private final static synthetic [LSeason; $VALUES
// access flags 0x9
public static values()[LSeason;
L0
LINENUMBER 3 L0
GETSTATIC Season.$VALUES : [LSeason;
INVOKEVIRTUAL [LSeason;.clone ()Ljava/lang/Object;
CHECKCAST [LSeason;
ARETURN
MAXSTACK = 1
MAXLOCALS = 0
// access flags 0x9
public static valueOf(Ljava/lang/String;)LSeason;
L0
LINENUMBER 3 L0
LDC LSeason;.class
ALOAD 0
INVOKESTATIC java/lang/Enum.valueOf (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
CHECKCAST Season
ARETURN
L1
LOCALVARIABLE name Ljava/lang/String; L0 L1 0
MAXSTACK = 2
MAXLOCALS = 1
// access flags 0x2
// signature ()V
// declaration: void <init>()
private <init>(Ljava/lang/String;I)V
L0
LINENUMBER 3 L0
ALOAD 0
ALOAD 1
ILOAD 2
INVOKESPECIAL java/lang/Enum.<init> (Ljava/lang/String;I)V
RETURN
L1
LOCALVARIABLE this LSeason; L0 L1 0
MAXSTACK = 3
MAXLOCALS = 3
// access flags 0x8
static <clinit>()V
L0
LINENUMBER 4 L0
NEW Season
DUP
LDC "SPRING"
ICONST_0
INVOKESPECIAL Season.<init> (Ljava/lang/String;I)V
PUTSTATIC Season.SPRING : LSeason;
NEW Season
DUP
LDC "SUMMER"
ICONST_1
INVOKESPECIAL Season.<init> (Ljava/lang/String;I)V
PUTSTATIC Season.SUMMER : LSeason;
NEW Season
DUP
LDC "FALL"
ICONST_2
INVOKESPECIAL Season.<init> (Ljava/lang/String;I)V
PUTSTATIC Season.FALL : LSeason;
NEW Season
DUP
LDC "WINTER"
ICONST_3
INVOKESPECIAL Season.<init> (Ljava/lang/String;I)V
PUTSTATIC Season.WINTER : LSeason;
L1
LINENUMBER 3 L1
ICONST_4
ANEWARRAY Season
DUP
ICONST_0
GETSTATIC Season.SPRING : LSeason;
AASTORE
DUP
ICONST_1
GETSTATIC Season.SUMMER : LSeason;
AASTORE
DUP
ICONST_2
GETSTATIC Season.FALL : LSeason;
AASTORE
DUP
ICONST_3
GETSTATIC Season.WINTER : LSeason;
AASTORE
PUTSTATIC Season.$VALUES : [LSeason;
RETURN
MAXSTACK = 4
MAXLOCALS = 0
}
Java
복사
Enum 클래스에 정의된 메서드
public static void main(String[] args) {
Season spring = Season.SPRING;
// values() 메소드는 해당 enum에 정의된 모든 상수를 배열로 리턴해준다.
Season[] seasons = Season.values();
Arrays.stream(seasons).forEach(System.out::println);
// valueOf() 메소드는 해당 enum에 정의된 상수 이름을 넣어주면 해당 enum 상수를 리턴해준다.
System.out.println(Season.valueOf("SPRING"));
Arrays.stream(seasons).forEach(s -> System.out.println(Season.valueOf(s.toString())));
// ordinal() 메소드는 해당 enum에 정의된 상수를 리턴해준다.
System.out.println(Season.SPRING.ordinal());
// getDeclaringClass() 메소드는 해당 enum이 정의된 enum 타입을 리턴해준다.
System.out.println(spring.getDeclaringClass());
// name() 메소드는 해당 enum에 정의된 상수 이름을 리턴해준다.
System.out.println(spring.name());
}
Java
복사
EnumSet
: 열거형 타입과 함께 사용하기 위한 Set 구현체다. 상수 그룹을 나타내기 위한 목적으로 사용된다.
: enum 값만 사용할 수 있으며 모든 값은 동일한 enum 객체에 속해야 한다.
: thread-safe하지 않으므로 동기화가 필요할 수 있다. 최소한 Collections.synchronizedSet() 메서드를 통해 EnumSet을 래핑해주자.
•
EnumSet의 메서드
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType)
매개변수로 받은 요소 타입을 사용하여 비어있는 EnumSet을 생성한다.
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)
매개변수로 받은 타입의 모든 요소(열거형 상수들)를 포함하는 EnumSet을 생성한다.
public static <E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)
매개변수로 받은 EnumSet과 동일한 타입을 사용하여 동일한 요소를 포함하는 EnumSet을 생성한다.
public static <E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)
매개변수로 받은 Collection으로 초기화 된 EnumSet을 생성한다.
public static <E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
매개 변수의 EnumSet<E> s 에 포함되어 있지 않은 요소들을 포함하는(차집합) EnumSet을 생성한다.
public static <E extends Enum<E>> EnumSet<E> of(E e1)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)
매개변수로 받은 요소를 포함하는 열거형 집합을 생성한다.
@SafeVarargs
public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest)
가변인자 기능을 사용하는 of 메서드의 여섯번째 오버로딩, 가변인자를 사용하지 않는 of 메서드보다 느리게 실행될 수 있다.
public static <E extneds Enum<E>> EnumSet<E> range(E from, E to)
매개변수 from과 to의 범위의 모든 요소를 포함하는 EnumSet을 생성한다. 반환된 set에는 to까지 포함됩니다.
Java
복사
JPA를 사용할 때, enum 주의점
•
클래스 내부에 Enum 클래스를 사용할 수도 있다.
•
ordinal은 enum의 순서를 의미하고 내부적인 순서를 의미하는 것이다.
•
엔티티의 항목으로만 정의 해놓게 된다면? 내부적으로 ordinal 값을 데이터로 활용하게 된다.
→ ordinal은 내부적으로 사용하는 값임으로 원하지 않는 결과가 만들어질 수 있다.
enum Fruit{
Kiwi, Apple, Banana
}
@Entity
class MyFavorite{
@Id @GeneratedValue
Integer id;
@Enumerated(EnumType.STRING)
Fruit fruit;
}
Java
복사
•
위와 같이 @Enumerated 라는 애노테이션을 활용하여 enum임을 명확히 정의해줘야한다.