본문 바로가기
Java/정리

[Java] enum의 정의와 활용

by 콧등치기국수 2023. 8. 4.

작년에 엑셀 다운로드 모듈을 만들 때 cell의 속성과 관련한 부분에서 enum을 적용했었다. 올해 엑셀 모듈에 새로운 구현 클래스를 추가해볼까 하고 살펴보던 중, enum에 대해 더 공부하고 블로그로 작성하면 좋을 것 같아 이렇게 정리한다.
 

🧊 enum(열거형) 이란?

열거형은 서로 관련된 상수를 편리하게 선언하기 위한 것으로 여러 상수를 정의할 때 사용하면 유용하다.
일정 개수의 상수 값을 정의한 다음, 그 외의 값은 허용하지 않는다.
 
.java확장자를 사용하며, 클래스이기 때문에 생성자를 가질 수 있다. 생성자의 접근제어자는 private으로 외부에서 인스턴스화할 수 없다. 그리고 마찬가지로 내부에서도 열거형을 인스턴스화할 수 없다.
 
필드도 추가할 수 있고, 일반 메소드를 생성할 수 있다. 만약 필드를 생성했다면 그에 맞게 생성자를 꼭 생성해주어야한다.


열거형을 정의하는 방법은 아래와 같다.

enum 열거형이름 {상수1, 상수2, ...}

아래 예시를 통해 가장 간단히 열거형을 정의한 형태를 살펴보자.

//1번
public enum Day {
  MON, TUE, WED, THU, FRI, SAT, SUN;   //상수이므로 관례에 따라서 대문자로 작성. 대문자 스네이크 케이스 사용. 
}

//2번
public enum Day {
  MON, TUE, WED, THU, FRI, SAT, SUN;
  
  private Day() {
    // TODO Auto-generated constructor stub
  }
}

열거형에 필드가 없는 경우는, 1번과 2번 모두 사용할 수 있다. 
생성자를 정의할 수 있는 이유가 뭘까?  열거형은 아래와 같이 생긴 클래스와 같기 때문이다.
즉 열거형에서는 상수 하나당 인스턴스가 생성된다.

class Day {
  public static final MON = new Day("Mon");
  public static final TUE = new Day("TUE");
  public static final WEN = new Day("WEN");
  public static final THU = new Day("THU");
    ...
}

열거형 상수 간 비교에는 '=='을 사용할 수 있다.
equals()가 아닌 '=='로 비교가 가능하기 때문에 그만큼 빠른 성능을 제공할 수 있다.

public class Unit {
  int x,y;
  Day day;          //열거형을 인스턴스 변수로 선언
  
  void init() {
    day = Day.SAT;  //열거형에 정의된 상수를 사용하는 방법: [열거형이름.상수명] , day를 Sat으로 초기화
  }
}

 
 

🧊 enum 은 왜 사용하는가?

1. 코드가 단순해지며 가독성이 좋아진다.
아래 클래스 Day와 열거형 Day의 기능은 같지만, enum이 한 눈에 들어올 만큼 단순한 구조이다. 

class Day {
	public static final MON = new Day("MON");
	public static final TUE = new Day("TUE");
	public static final WEN = new Day("WEN");
	public static final THU = new Day("THU");
    
    private String day_nm;
    
    private Day(String day_nm){
      this.day_nm = day_nm
    }
}

 

public enum Day {
  MON, TUE, WED, THU;

 
2. 키워드 enum을 사용하기 때문에 구현의 의도가 열거임을 분명히 나타낼 수 있다.
 
3. 인스턴스 상속과 생성을 제한할 수 있다.
열거형은 암시적으로 java.lang.Enum 클래스를 상속하는데, 자바에선 클래스의 다중 상속을 허용하지 않으므로 다른 열거형이나 클래스를 상속할 수는 없다.
클래스는 상속받을 수 없지만 인터페이스는 implements를 통해 상속받아 사용할 수 있다.
 
4. 인스턴스가 JVM 내에 하나만 존재한다는 것이 100% 보장되므로 싱글톤을 만드는 좋은 방법이다.
 
5. 특정 범위의 값만 사용가능하므로 컴파일 오류나 런타임 예외를 줄일 수 있어, 컴파일 타임에 타입 안정성을 보장한다.
그러므로 제한된 값 목록을 필요로 하는 경우 유용하다.
 
6. 값이 추가되거나 변경되는 경우, 한 곳에서만 변경하면 되기 때문에 코드의 유지 보수가 용이하다.
 
 

🧊 enum 활용

💧 필드, 메소드 추가

java에서 열거 타입은 필드와 메소드를 추가하여 사용할 수 있다.
생성자를 통해 필드를 초기화하기 때문에 필드의 접근 제한자는 private으로 하여 다른 곳에서 값을 변경하지 못하게 하여 사용할 수 있다.
 
일반적으로 클래스에서 필드를 사용하는 것처럼 필드를 명시하고 그에 맞는 생성자를 정의하면 된다.
이때, 각 열거 객체의 필드는 상수 이름 옆에 괄호 () 를 사용하여 적어준다.

public enum Day2 {
  MON("월요일", 5),
  TUE("화요일", 4),
  WED("수요일", 3),
  THU("목요일", 2),
  FRI("금요일", 1),
  SAT("토요일", 0),
  SUN("일요일", 0);

  private final String str;
  private final int dDay;
  
  private Day2(String string, int i) {
    this.str  = string;
    this.dDay = i;
  }
  
  public void dDayByWeekend() {
    System.out.println(str + "은 주말까지 " + dDay + "일 남았습니다.");
  }
}

 
만약 필드의 접근제한자가 public이면 어떻게 될까?
업무할 때는 쓰지 않겠지만 한 번 테스트해보았다.

public enum Day2 {
  MON("월요일", 5),
  TUE("화요일", 4),
  WED("수요일", 3),
  THU("목요일", 2),
  FRI("금요일", 1),
  SAT("토요일", 0),
  SUN("일요일", 0);
  
  public String str;
  public int dDay;
//  private final String str;
//  private final int dDay;
  
  private Day2(String string, int i) {
    this.str  = string;
    this.dDay = i;
  }
  
  public void dDayToWeekend() {
    System.out.println(str + "은 주말까지 " + dDay + "일 남았습니다.");
  }
}

Day2 열거형의 dDayToWeekend() 를 실행해보자. 

public static void main(String[] args) {
  Day2.MON.dDay = 1;
  Day2.MON.dDayToWeekend();
}

실행 결과는 다음과 같다.

//월요일은 주말까지 1일 남았습니다.

월요일은 주말까지 5일 남았음에도 불구하고 사용자 맘대로 1일로 변경이 된 것을 볼 수 있다.
아무리 월요일의 다음날이 주말이길 바란다고 해도 실제로 일어날 수 없는 일이다. 따라서 '1'로 임의로 변경할 수 있도록 하는 것은 막아야할 것이다. 즉 정해져 있는 값이 있는 경우 사용자마다 값을 바꿔서 사용할 수 있다면 enum을 정의하는 의미가 없을 것이다.
따라서 열거형에서 필드의 접근제한자는 private으로 설정해주어야 한다.


위에서 정의한 Day와 Day2 열거형을 필드로 하는 Unit 클래스를 생성하고 활용해보자.
먼저 getDarkness(Day2 day) 메소드를 살펴보면 열거형으로 switch문을 사용하는 방식을 볼 수 있다.

public class Unit {
  int dark_index = 0;
  Day day;
  Day2 day2;
  
  public Unit() {};
  
  void init() {
    day = Day.MON;
    day2 = Day2.MON;
  }
  
  void getDarkness(Day2 day2) {
    switch (day2) {
      case Mon: dark_index = day2.dDay;  //case day2.Mon 가 아니다.
        break;
      case Tue: dark_index = day2.dDay;
        break;
      case Wed: dark_index = day2.dDay;
        break;
      case Thu: dark_index = day2.dDay;
        break;
      case Fri: dark_index = day2.dDay;
        break;
      case Sat: dark_index = day2.dDay;
        break;
      case Sun: dark_index = day2.dDay;
        break;
      default:
        throw new IllegalArgumentException("Invalid day2 : " + day2);
    }
    
    System.out.println(day2.str + "의 낯빛 어두움 지수 : " + dark_index);
  }
}

Day2 열거형에 정의된 상수인 Mon, Wed, Sat를 이용하여 getDarkness()메소드를 실행했다.
getDarkness()의 switch문에 따라 출력된 내용을 확인할 수 있다.

Unit u = new Unit();
u.getDarkness(Day2.MON);
u.getDarkness(Day2.WED);
u.getDarkness(Day2.SAT);

실행 결과는 다음과 같다.

//월요일의 낯빛 어두움 지수 : 5
//수요일의 낯빛 어두움 지수 : 3
//토요일의 낯빛 어두움 지수 : 0

 
 

🧊 enum 과 JVM

추후 업데이트할 예정이다.
 
 
 
참고
1. https://johngrib.github.io/wiki/java/enum/
2. https://blog.hexabrain.net/393
3. https://hudi.blog/java-enum/
4. https://www.nextree.co.kr/p11686/
5. https://velog.io/@mooh2jj/Java-Enum을-사용하는-이유
6. 자바의 정석(남궁성, 도우출판)