📌 Goal
- Enum의 기본적인 개념을 이해하고 설명할 수 있다.
- Enum이 등장하게 된 배경에 대해서 이해하고, 그 장점에 대해 설명할 수 있다.
- Enum의 문법 요소를 이해하고 적절하게 사용할 수 있다.
📌 열거형(enum)
열거형(enum)은 서로 연관된 상수들의 집합을 의미한다.
상수는 변하지 않는 값을 의미하며 final 키워드를 사용하여 선언할 수 있다. 열거형은 이러한 상수들을 보다 간편하게 관리하기 위해 사용할 수 있는 자바의 문법 요소이다.
JDK 1.5 이전 버전에서는 enum 문법을 지원하지 않는다. 그래서 여러 상수를 정의하기 위해서는 public static final을 통해 전역 변수로 상수를 설정하여 사용했다.
public static final int SPRING = 1;
public static final int SUMMER = 2;
public static final int FALL = 3;
public static final int WINTER = 4;
public static final int DJANGO = 1;
public static final int SPRING = 2; // 계절의 SPRING과 중복 발생!
public static final int NEST = 3;
public static final int EXPRESS = 4;
그러나 위의 코드와 같이, 정수 값을 통해 상수를 할당하면 상수명이 중복되는 경우가 종종 발생한다.
이러한 문제는 인터페이스를 사용하여 상수를 구분함으로써 다음과 같이 일차적으로 해결할 수 있다.
interface Seasons {
int SPRING = 1, SUMMER = 2, FALL = 3, WINTER = 4;
}
interface Frameworks {
int DJANGO = 1, SPRING = 2, NEST = 3, EXPRESS = 4;
}
그러나 이러한 경우 중복상수명의 문제는 피할 수 있지만, 타입의 안전성이라는 새로운 문제가 발생한다.
예를 들면 Seasons.SPRING 의 정수 값 1과 Frameworks.SPRING의 정수 값 2는 상수를 열거하기 위해 임의로 주어졌지만 다음과 같은 코드에 에러가 발생하지 않는다.
if (Seasons.SPRING == Frameworks.SPRING) {...생략...}
Seasons.SPRING 과 Frameworks.SPRING은 의미적으로 다른 개념임에도 불구하고, 이 둘을 비교하면 에러가 발생하지 않는다. 때문에 타입의 안전성이 떨어질 수 밖에 없다.
이 문제를 해결하기 위해서는 아래와 같이 서로 다른 객체로 만들어주어야 한다.
class Seasons {
public static final Seasons SPRING = new Seasons();
public static final Seasons SUMMER = new Seasons();
public static final Seasons FALL = new Seasons();
public static final Seasons WINTER = new Seasons();
}
class Frameworks {
public static final Frameworks DJANGO = new Frameworks();
public static final Frameworks SPRING = new Frameworks();
public static final Frameworks NEST = new Frameworks();
public static final Frameworks EXPRESS = new Frameworks();
}
위의 코드와 같이 객체를 생성해주면, 상수명의 중복과 타입 안전성 문제를 모두 해결할 수 있다. 그러나 코드가 굉장히 길어지고, 사용자 정의 타입이기 때문에 switch문에 사용할 수 없다는 문제가 발생한다.
이러한 맥락에서, 이같은 문제들을 효율적으로 해결하기 위해 만들어진 것이 바로 enum이다.
enum Seasons { SPRING, SUMMER, FALL, WINTER }
enum Frameworks { DJANGO, SPRING, NEST, EXPRESS }
enum을 사용하면 앞서 발생했던 문제들을 모두 효과적으로 해결할 수 있을 뿐 아니라 코드를 단순하고 가독성 좋게 만들 수 있다. 또한 enum으로 정의한 상수는 switch문에서도 사용이 가능하다.
아래의 코드는 enum을 활용해 switch문에 적용해본 예제 코드이다.
enum Seasons {SPRING, SUMMER, FALL, WINTER}
public class Main {
public static void main(String[] args) {
Seasons seasons = Seasons.SPRING;
switch (seasons) {
case SPRING:
System.out.println("봄");
break;
case SUMMER:
System.out.println("여름");
break;
case FALL:
System.out.println("가을");
break;
case WINTER:
System.out.println("겨울");
break;
}
}
}
//출력값
봄
위의 예제 코드처럼 enum을 사용하면 switch문을 사용할 수 있다는 것을 확인할 수 있다.
정리하자면,
자바에서 열거형은 여러 상수들을 보다 편리하게 선언하고 관리할 수 있게하며, 상수의 중복을 피하고, 타입에 대한 안정성을 보장한다.
같은 효과를 낼 수 있는 다른 코드에 비해 훨씬 더 간결하고 가독성이 좋은 코드를 작성할 수 있으며, switch문에서도 작동이 가능하다.
📌 열거형(Enum)의 사용
✔️ 열거형(Enum)의 선언
enum 열거형이름 { 상수명1, 상수명2, 상수명3, ...}
사계절을 상수로 정의한다면 다음과 같다. 참고로 상수는 관례적으로 대문자로 작성한다.
enum Seasons {
SPRING, //정수값 0 할당
SUMMER, //정수값 1 할당
FALL, //정수값 2 할당
WINTER //정수값 3 할당
}
각각의 열거 상수들은 객체이기 때문에, 위의 예제 코드에서 Seasons라는 이름의 열거형 SPRING, SUMMER, FALL, WINTER는 총 네 개의 열거 객체를 포함하고 있다.
마지막으로, 각각의 상수들에는 따로 값을 지정하지 않아도 자동적으로 0부터 시작하는 정수 값이 할당되어 각각의 상수를 가리키게 된다.
✔️ 열거형(Enum)의 사용
아래의 예제 코드를 살펴보자.
enum Seasons { SPRING, SUMMER, FALL, WINTER }
public class EnumExample {
public static void main(String[] args) {
System.out.println(Seasons.SPRING); // SPRING
}
}
열거형에 선언된 상수에 접근하는 방법은 열거형이름.상수명을 통해 가능하다. 클래스에서 static 변수를 참조하는 것과 동일하다고 할 수 있다.
이제 열거형 상수를 새로운 참조변수에 담아보자.
enum Level {
LOW, // 0
MEDIUM, // 1
HIGH // 2
}
public class Main {
public static void main(String[] args) {
Level level = Level.MEDIUM;
switch(level) {
case LOW:
System.out.println("낮은 레벨");
break;
case MEDIUM:
System.out.println("중간 레벨");
break;
case HIGH:
System.out.println("높은 레벨");
break;
}
}
}
//출력값
중간 레벨
위의 예제 코드에서,
Level이라는 열거형을 만들었고, 그 안에 세 가지의 열거 상수(LOW, MEDIUM, HIGH)를 선언해주었다. 그리고 열거형과 같은 타입의 참조 변수 level에 Level.MEDIUM 값을 할당하고 switch문을 통해 해당 값에 대한 출력 값을 얻을 수 있었다.
이처럼 enum을 사용하면 변경되지 않는 한정적인 데이터들을 효과적으로 불러올 수 있다.
✔️ 열거형(Enum)에서 사용되는 메서드
아래 메서드들은 모든 열거형의 조상인 java.lang.Enum에 정의되어있는 것으로, 클래스에서 최상위 클래스 Object에 정의된 메서드들을 사용할 수 있었던 것과 동일하다고 할 수 있다.
리턴 타입 | 메소드(매개변수) | 설명 |
String | name() | 열거 객체가 가지고 있는 문자열을 리턴하며, 리턴되는 문자열은 열거타입을 정의할 때 사용한 상수 이름과 동일하다. |
int | ordinal() | 열거 객체의 순번(0부터 시작)을 리턴한다. |
int | compareTo(비교값) | 주어진 매개값과 비교해서 순번 차이를 리턴한다. |
열거 타입 | valueOf(String name) | 주어진 문자열의 열거 객체를 리턴한다. |
열거 배열 | values() | 모든 열거 객체들을 배열로 리턴한다. |
댓글