본문 바로가기
Language/Java

[Java] Enumeration

by 기몬식 2023. 9. 4.

Enumeration

출처

Enum은 고유한 값의 집합을 나타내는 자료형으로 클래스와 비슷한 구조로 선언되며 상수를 정의하고 관리하는데 사용됩니다.
즉 Enum은 변수를 미리 정의하여 상수 집합이 될 수 있도록 해주는 특별한 데이터 유형입니다.
Enum이 도입된 Java 5 버전 이전에는 상수를 관리하기 위해 final static 변수를 사용하는 것이 일반적이었습니다.
Enum은 이러한 상수 관리를 보다 쉽고 효율적으로 사용할 수 있도록 기능을 제공합니다.

등장 배경

Enum이 등장하기 이전에는 상수를 정의할 때 주로 다음과 같은 방법을 사용했습니다.

interface Day {
  int MONDAY = 1;
  int TUESDAY = 2;
  int WEDNESDAY = 3;
  int THURSDAY = 4;
  int FRIDAY = 5;
  int SATURDAY = 6;
  int SUNDAY = 7;
}

위와 같은 상수 인터페이스를 정의하여 사용하면 변수명을 통해 해당 상수가 의미하는 값이 무엇인지 파악은 가능하나 중복된 값의 정의를 방지하기 어렵습니다.
또한 다른 클래스에서 동일한 상수 값을 정의하면 컴파일러은 아무런 문제없이 코드를 컴파일하지만 논리적인 오류를 가지게 됩니다.

추가로 위와 같은 상수 클래스 외에 달(Month)을 나타내는 새로운 상수를 정의한다고 가정하겠습니다.

interface Month {
  int JANUARY = 1;
  int FEBRUARY = 2;
  int MARCH = 3;
  int APRIL = 4;
  int MAY = 5;
  int JUNE = 6;
  int JULY = 7;
  int AUGUST = 8;
  int SEPTEMBER = 9;
  int OCTOBER = 10;
  int NOVEMBER = 11;
  int DECEMBER = 12;
}

Day 인터페이스와 Month 인터페이스는 각자 서로 다른 두 개의 특징을 가지는 상수 인터페이스로 선언되었지만 문제점이 있습니다.
바로 서로 다른 상수 인터페이스에서 정의된 상수들간 값 비교를 할 수 없다는 것입니다.
다른 인터페이스를 비교하면 컴파일 단계에서는 아무런 문제없이 컴파일이 되지만 이는 런타임 시점에 예기치 못한 문제를 발생시킬 수 있습니다.
들어오는 값과 비교하는 값이 일치하지 않는 타입을 가지고 있으면 특정 로직에서 문제가 발생할 수 있습니다.

그렇다면 위의 경우의 각 상수들의 타입을 자기 자신의 상수 클래스로 정의해 보겠습니다.

class Day {
  public static final Day MONDAY = new Day();
  public static final Day TUESDAY = new Day();
  ...
}

class Month {
  public static final Month JANUARY = new Month();
  public static final Month FEBRUARY = new Month();
  ...
}

public static void main(String[] args) {
  System.out.Println(Day.MONDAY == Month.FEBRUARY)
}

Day 클래스와 Month 클래스의 필드의 타입은 자기 자신의 클래스를 인스턴스화한 값으로 선언했습니다.
각각의 필드는 같은 데이터 타입을 가지지만 서로 다른 데이터 값을 가지고 있게 됩니다.
main()에서와 같이 다른 타입을 비교하게 되면 컴파일 시점에서 에러가 발생합니다.
런타임 시점에서 발생할 수 있는 에러인 서로 다른 데이터 타입은 비교할 수 없다는 내용의 에러가 드디어 컴파일 시점에서 캐치할 수 있게 된 것입니다.

하지만 여기서 한가지 문제점이 발생합니다.
사용되는 상수는 제한되는 데이터 타입때문에 swith문과 같은 조건 - 판단문을 사용하지 못해 각 타입을 비교하지 못합니다.
따라서 위와 같은 문제점들을 해결하기 위한 방법 중 하나가 Enumeration입니다.

활용

생성


enum Day {
  MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

enum Month {
  JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER
}

Enum 키워드를 사용해 열거형 집합을 생성하는 방법입니다.
위의 방식보다 훨씬 간결하고 가독성이 더 좋아졌으며 인스턴스 생성과 상속을 방지합니다.
또한 enum이라는 예약어를 통해 해당 클래스의 목적이 열거형임을 분명하게 나타낼 수 있습니다.

특징

private 생성자

Enum은 일반적인 생성자와는 다르게 상수를 초기화하는데 내부적으로 생성자를 사용합니다.
내부적으로 생성자를 사용하기때문에 기본적으로 접근 제어자는 private을 가집니다.

Enum은 상수 집합을 나타내기 위한 자료형이기 때문에 열거형 집합은 불변성을 보장해야합니다.
따라서 Enum은 상수 집합의 불변성을 보장하기 위해 상수를 외부에서 추가로 생성하거나 변경하지 못하도록 제한합니다.
최초로 상수의 속성은 컴파일 시점에 상수가 정의될 때 초기화되고 그 이후에는 값이 변경되지 않는 불변성을 가집니다.
따라서 멀티 스레드 환경에서 Thread Safe하게 사용할 수 있습니다.

정리하자면 클라이언트는 Enum은 인스턴스가 존재하지 않으며 생성도 할 수 없고 상속을 받을 수도 없지만 선언된 Enum 상수는 항상 존재합니다.

뛰어난 조건-판단문

Enum 상수는 명확한 의미를 가지기 때문에 switch 문을 통해 상수에 대한 동작을 명확하게 파악할 수 있습니다.
또한 간결한 표현은 코드의 유지보수성이 높아이고 선택지를 명시적으로 나타냄으로 휴먼 에러를 방지합니다.

편리한 순회 가능

Enum은 values() 와 같은 메소드로 순회 기능을 편리하게 지원하는데 이를 통해 Enum 상수를 반복하여 처리할 수 있습니다.
여러 가지 선택지를 나타내는 경우 순회 기능을 활용하여 각 선택지에 대한 동작을 수행하거나 처리할 수 있습니다.

추가 속성

enum Color {
  RED("Red", "빨강"),
  GREEN("Green", "초록");

  private final String color;
  private final String description;

  Color(String color, String description) {
    this.color = color;
    this.description = description;
  }

  public String getColor() {
    return this.color
  }

}

Enum은 생성자의 파라미터를 통해 추가 속성을 필드에 설정해주고 getter와 같은 프로퍼티를 반환하는 메소드를 제공할 수도 있습니다.
이와 같이 enum 상수 집합들을 메소드나 필드에 활용하여 추가적으로 열거형 상수들을 활용한 기능을 제공해주는 식으로 제공될 수 있습니다.

정리

정리하자면 Java 5 버전 이전 Enum이 등장하기 전에 상수를 관리하는 방법을 순차적으로 알아봤습니다.
또한 Enum의 기본적인 특징을 알아봤습니다.
위와 같은 내용들을 걸쳐 Java Enum은 고유한 값의 집합을 효과적으로 관리하는 강력한 도구로 코드의 가독성을 향상시키고 유지보수성을 높이며 상수의 중복을 방지하고 안전한 방식으로 상수를 다룰 수 있는 기능을 제공한다는 사실을 알게됐습니다.

오탈자 및 오류 내용을 댓글 또는 메일로 알려주시면, 검토 후 조치하겠습니다.