Search
Duplicate
🏟️

Enum

태그
개념
정의
상위 항목
하위 항목

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임을 명확히 정의해줘야한다.