Java에서 Enum이란?
enum은 enumeration이라는 셈, 계산, 열거, 목록이라는 뜻의 앞부분만 따서 만든 자바의 예약어이다.
JDK 1.5 부터 등장한 열거형 상수이다. 서로 연관된 상수들의 집합이라고 생각할 수 있다.
(그냥 관련 있는 상수들을 하나로 모아두었다고 생각하면 쉽다)
자바에서 상수를 정의하는 방법
상수는 변하지 않는 값이다. 즉, 기본 자료형의 값을 고정하는 것을 상수라고 한다.
자바에서 상수를 정의하는 방법은 다양하다.
1. 임의의 값을 지정하여 상수처럼 쓰기
/*
* 월요일 = 1
* 화요일 = 2
* 수요일 = 3
* ...
*/
int day = 1;
switch (day) {
case 1:
System.out.println("월요일");
break;
case 2:
System.out.println("화요일");
break;
case 3:
System.out.println("수요일");
break;
...
}
여기서는 변하지 않는 int 값에 따라 요일을 구별한다
하지만 주석으로 구분해야하기 때문에 가독성이 좋지 않고, 실수도 할 수 있다.
2. private static 상수 정의
class Day {
private static final int MONDAY = 1;
private static final int TUESDAY = 2;
private static final int WEDNESDAY = 3;
public static void method() {
int day = MONDAY;
switch (day) {
case MONDAY:
System.out.println("월요일");
break;
case TUESDAY:
System.out.println("화요일");
break;
case WEDNESDAY:
System.out.println("수요일");
break;
}
}
}
- 클래스 내부에서 상수는 관례적으로 private static final로 정의한다
- static final로 설정함으로써 불변하면서 메모리에 한 번만 할당될 수 있도록 설정한다
- 하지만 이 방식은 소스코드에 상수를 계속 추가해야한다
- 한 눈에 상수들이 어떤 것에 관련된 것인지 보기 힘들고
- 각각의 상수의 집합에서 같은 이름으로 정의된 상수가 있다면 중복된 이름이라 컴파일 단계에서 오류가 발생한다
- 그래서 class또는 인터페이스를 사용하여 각각의 집합끼리 상수가 정의한다
3. 인터페이스 사용
interface DAY {
int MONDAY = 1;
int TUESDAY = 2;
int WEDNESDAY = 3;
}
interface MONTH {
int JANUARY = 1;
int FEBRUARY = 2;
int MARCH = 3;
}
class Day {
public static void method() {
int day = DAY.MONDAY;
switch (day) {
case DAY.MONDAY:
System.out.println("월요일");
break;
case DAY.TUESDAY:
System.out.println("화요일");
break;
case DAY.WEDNESDAY:
System.out.println("수요일");
break;
}
if (DAY.MONDAY == MONTH.JANUARY) {
System.out.println("월요일과 1월은 같다");
}
}
}
이렇게 하면 하나하나 상수로 지정해주는 것보다 깔끔하다.
하지만 이 방식은 아래 코드처럼 다른 타입끼리도 비교할 수 있다는 단점이 있다.
이렇게 다른 타입끼리 비교해도 컴파일 단계에서 잡아주지 않기 때문에 런타임에 예상하지 못한 문제를 발생시킬 수 있다.
4. 상수를 정의하는 클래스 정의(상수 패턴)
class DAY {
public static final int MONDAY = 1;
public static final int TUESDAY = 2;
public static final int WEDNESDAY = 3;
}
class MONTH {
public static final int JANUARY = 1;
public static final int FEBRUARY = 2;
public static final int MARCH = 3;
}
class Day {
public void method() {
// if (DAY.MONDAY == MONTH.JANUARY) { //에러발생
// System.out.println("월요일과 1월은 같다");
// }
DAY day = DAY.MONDAY;
switch (day) { //에러발생
case DAY.MONDAY:
System.out.println("월요일");
break;
case DAY.TUESDAY:
System.out.println("화요일");
break;
case DAY.WEDNESDAY:
System.out.println("수요일");
break;
}
if (DAY.MONDAY == MONTH.JANUARY) {
System.out.println("월요일과 1월은 같다");
}
}
}
이렇게 하면 다른 타입끼리 비교할 수 없게 만들 수 있다.
하지만 DAY타입은 switch문의 비교값으로 사용될 수 없어서 일일이 if문으로 비교해줘야한다.
하지만 enum을 사용하면 switch문도 사용이 가능하다!
5. 오늘의 주인공, enum사용
enum DAY {
MONDAY, TUESDAY, WEDNESDAY;
}
enum MONTH {
JANUARY, FEBRUARY,MARCH;
}
class Day {
public void method() {
DAY day = DAY.MONDAY;
switch (day) {
case MONDAY:
System.out.println("월요일");
break;
case TUESDAY:
System.out.println("화요일");
break;
case WEDNESDAY:
System.out.println("수요일");
break;
}
}
}
enum을 사용하면 다른 타입끼리 비교도 안되고, 상수값이므로 switch문도 사용할 수 있다.
정의문 자체도 굉장히 깔끔하고 가독성이 좋다.
Enum의 장점
위에서 본 장점을 정리해보면 다음과 같다
- Enum끼리 비교할 때 실제 값뿐만 아니라 타입까지 체크함
- 코드가 단순해지며 가독성이 좋다
- enum 키워드 사용으로 구현의 의도가 열거임을 분명하게 나타낸다
- 인스턴스 생성과 상속을 방지한다 -> 상수의 역할 충실히 수행 + 싱글톤으로 메모리 절약
- 열거체의 상숫값이 재정의되더라도 다시 컴파일할 필요 없음
- 허용 가능한 값을 제한할 수 있다
Enum의 사용법
enum Rainbow {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
이렇게 정의된 Enum의 경우, 첫 번째 상숫값은 0으로 정의되고 그 다음 값들은 1, 2, 3, ... 이 설정된다
즉 RED의 상숫값은 1이고, ORANGE는 2, YELLOW는 3이다
상숫값 직접 설정하기
enum Rainbow {
RED(1), ORANGE(333), YELLOW(2), GREEN(45), BLUE(3), INDIGO(10), VIOLET(7);
private final int value;
Rainbow(int value) {
this.value = value;
}
public int getValue() {
return value;
}
}
- 불규칙한 상숫값을 배정하고 싶다면, 상수 옆에 괄호를 추가하는 방식을 사용할 수 있다
- 그리고 해당 상숫값을 저장할 수 있는 인스턴스 변수와 생성자를 꼭 추가해야한다 (안그러면 에러남)
- 그리고 해당하는 상숫값을 가져올 수 있게 getter도 만들어주면 좋겠다!
추가속성 부여하기
enum MONTH {
JANUARY(1),
FEBRUARY(2),
MARCH(3);
private int number;
MONTH(int number) {
this.number = number;
}
}
위에서 상수값을 지정해주듯이, 추가속성도 지정해줄 수 있다.
enum MONTH {
JANUARY(1, "겨울"),
FEBRUARY(2, "겨울"),
MARCH(3, "봄");
private int number;
private String season;
MONTH(int number, String season) {
this.number = number;
this.season = season;
}
public int getNumber() { return number; }
public String getSeason() { return season; }
}
다음과 같이 계절정보도 넣어 다양한 추가속성을 사용할 수 있다.
메소드나 필드를 enum타입에 추가함으로써 enum상수에 어떤 데이터도 연관시킬 수 있다!
enum은 상수를 모아놓는 용도 외에도 이렇게 관련데이터를 모으는 추상체가 될 수 있다.
Enum클래스 메소드 종류
java.lang.Enum클래스는 모든 자바 열거체의 공통된 조상 클래스이다. Enum을 만들면 무조건 이 클래스를 상속받는다.
이 Enum클래스에는 활용가능한 다양한 메소드가 있다!
1. values()
Rainbow[] values = Rainbow.values();
for (Rainbow rainbow : values) {
System.out.println(rainbow);
}
- values() 는 열거체의 모든 상수를 저장한 배열을 return한다
- Enum의 모든 상수값을 가져오고 싶을 때 사용하면 좋겠다
2. valueOf()
enum Rainbow {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
public class Main {
public static void main(String[] args) {
Rainbow rainbow = Rainbow.valueOf("RED");
System.out.println(rainbow);
}
}
- valueOf() 는 전달된 문자열과 일치하는 Enum의 상수를 반환한다
- 상수에 해당하는 RED가 반환된 걸 볼 수 있다
3. ordinal()
enum Rainbow {
RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}
public class Main {
public static void main(String[] args) {
int idx = Rainbow.BLUE.ordinal();
System.out.println(idx); //4
}
}
- ordinal() 은 해당 열거체 상수가 열거체 정의에서 정의된 순서(0부터 시작)을 반환한다
- Enum 안에서 나의 상대적인 위치를 반환한다고 볼 수 있다 (상숫값을 반환하는 게 아님!!!)
- ordinal()에서 상숫값 자체는 전혀 쓸모가 없다
enum Rainbow {
RED(1), ORANGE(333), YELLOW(2), GREEN(45), BLUE(13), INDIGO(100), VIOLET(77);
private final int value;
Rainbow(int value) { this.value = value; }
public int getValue() { return value; }
}
public class Main {
public static void main(String[] args) {
int idx = Rainbow.BLUE.ordinal();
System.out.println(idx); //4
}
}
- 다음과 같이 상숫값이 4가 아니어도 나의 상대적인 위치인 4를 반환한다
- 상숫값에 상관없이 나의 상대적 위치를 반환한다는 게 포인트다 (상숫값 중에서 BLUE는 3번째로 작음, 전혀 상관없는 이야기!)
4. name()
String name = Rainbow.BLUE.name();
System.out.println(name); //BLUE
- 딱 봐도 알겠지만 이름을 반환한다
Enum 더 알아보기
- enum타입의 생성자는 private으로 고정이다
- 고정된 상수의 집합으로, 컴파일시간에 모든 값을 알고있어야한다
- 다른 곳에서 동적으로 어떤 값을 지정할 수 없으므로, 컴파일 시 타입 안정성이 보장된다
- 외부 접근 가능한 생성자가 없으므로, 실제적으로 final처럼 동작한다(인스턴스 생성불가능, 상속 불가능)
- 싱글톤을 구현한다고 볼 수 있다
- static + final이 붙은 것처럼 동작 = 상수의 역할을 충실히 수행한다
- enum과 switch구문은 찰떡궁합이다
- switch문의 구분자로 사용될 수 있고, case문도 가독성이 아주 좋다
- if문을 줄일 수 있다
- java뿐만 아니라 C, Perl, C#, C++, GO 등에서도 enum을 사용한다
참고자료들
'프로그래밍 언어 > Java' 카테고리의 다른 글
[JAVA] Test로 Collections 공부하기 (0) | 2023.11.07 |
---|---|
[JAVA] 상수와 enum 공부하기2 (0) | 2023.11.07 |
[JAVA] 상수를 private static final로 선언하는 이유 (0) | 2023.11.07 |
[JAVA] 단위테스트 공부3: 활용하기 (0) | 2023.11.01 |
[JAVA] 클래스 내부 속성으로 가지고 있는 객체를 mock으로 설정하는 방법: @InjectMock이 동작하지 않는 이유 (0) | 2023.11.01 |