[JAVA] Enum: 열거형

Enum이란?

  • Enum은 열거형(Enumerated Type)을 의미하는 단어로, java 1.5부터 문법적으로 지원하기 시작하였다.
  • Enum은 class이다.
     -> Enum만의 문법적 형식을 가지기 때문에 class가 아닌 ‘enum’ 키워드를 사용한다.
  • Enum은 private 생성자만 가질 수 있어, 외부에서의 인스턴스 생성을 막는다.
  • Enum은 싱글톤을 만드는 가장 좋은 방법 중 하나로 권장된다.
  • Enum 인스턴스는 리플렉션을 통해 생성할 수 없다.
     -> java.lang.reflect.Constructor.newInstance() 메서드 내에서 Enum이면 IlleaglArgumentException 예외가 발생하도록 처리되어 있다.

Enum = Class

enum Fruit {
  APPLE, PEACH, BANANA; // field
}

위의 코드는 아래와 같다.

class Fruit {
  public static final Fruit APPLE = new Fruit();
  public static final Fruit PEACH = new Fruit();
  public static final Fruit BANANA = new Fruit();
  private Fruit(); // 인스턴스 생성 불가
}

결국, enum의 각 필드(APPLE, PEACH, BANANA)는 서로 다른 인스턴스이다.

Enum은 private 생성자만 가질 수 있다.

enum Fruit {
  APPLE, PEACH, BANANA;

  Fruit() { // == private Fruite() {
    System.out.println("Call Constructor " + this);
  }
}

public class EnumTest {

  public static void main(String[] args) {
    Fruit type = Fruit.APPLE;
  }
}
// result
Call Constructor APPLE
Call Constructor PEACH
Call Constructor BANANA

생성자 응용

enum Fruit {

  APPLE("red"), PEACH("pink"), BANANA("yellow");

  private String color;

  Fruit(String color) {
    this.color = color;
  }

  String getColor() {
    return this.color;
  }
}

public class EnumTest {

  public static void main(String[] args) {
    for(Fruit f : Fruit.values()) { // values는 enum 내장 메서드
      System.out.println(f+", " + f.getColor());
    }
  }
}
// result
APPLE, red
PEACH, pink
BANANA, yellow

Enum 내장 메서드

enum Fruit {
  APPLE, PEACH, BANANA; // field
}

values()

enum 원소들을 순서대로 enum 타입으로 리턴 (ENUM$VALUES를 복사하기 때문에 자주 호출하지 않는 것이 좋다.)

for(Fruit f : Fruit.values()) { // values는 enum 내장 메서드
  System.out.println(f);
}

// result
APPLE
PEACH
BANANA

oridinal()

원소에 열거된 순서를 정수 값으로 리턴한다.

System.out.println(Fruit.APPLE.oriainal()) // 0

valueOf()

매개변수로 넘기는 Strign과 열거형에서 일치하는 이름을 갖는 원소를 리턴한다.

System.out.println(Fruit.valueOf("BANANA")); // BANANA

Enum으로 싱글톤 패턴 구현하기

enum Singleton {
		INSTANCE;
		int value;
		
		public int getValue() {
			return value;
		}

		public void setValue(int value) {
			this.value = value
		}
	}

public class EnumSingletonExample {
	
	public static void main(String[] args) {

		Singleton singleton = Singleton.INSTANCE;

		singleton.setValue(10);

		System.out.println(singleton.getValue()); // 10
	}
}

Enum의 역직렬화

Enum 타입의 역직렬화는 일반적인 객체의 역직렬화와는 다른 방식으로 처리된다. 일반적으로 객체의 역직렬화는 Serializable 인터페이스를 구현하고 명시적으로 serialVersionUID 값을 선언해주어야 하지만, Enum 타입에서는 이러한 과정이 필요하지 않다. Enum 타입은 내부적으로 Serializable 인터페이스를 구현하고 있으며, serialVersionUID 기본값은 0L로 부여되기 때문에 명시적으로 선언하지 않아도 된다. 또한, 역직렬화 시 enum 상수의 이름을 사용하여 인스턴스를 생성하기 때문에 enum 상수의 위치나 순서를 사용하는 것이 아니므로 enum 상수가 추가되거나 삭제되어도 문제가 없다.

Enum을 사용하는 이유

  • 가독성이 좋다
     -> Enum 상수 자체가 해당 상수의 역할과 의미를 명확하게 나타내기 때문이다.
  • 코드의 실수를 줄여준다.
  • 인스턴스 생성과 상속을 방지한다.
  • 스레드 안전성을 보장한다.
  • 싱글톤을 보장한다
     - 리플렉션을 통한 인스턴스 생성이 불가능하다.
     - 역직렬화시 동일 인스턴스를 보장한다.
         -> Enum 타입 내부적으로 serializable을 구현하고
             -> serialVersionUID 기본 값이 0L으로 부여되며,
                 -> 역직렬화시 enum 상수의 이름을 사용하여 인스턴스를 생성하기 때문이다.

Enum 타입 vs Other 타입

“Enum 타입 이외의 타입들을 ‘Other 타입’이라고 정의하였다.”
  • Enum: 미리 정의된 ‘상수 값’만 가질 수 있다. (Other: 값을 가지는 모든 객체 생성 가능)
  • Enum: ‘상수 이름’으로 해당 상수의 값에 접근할 수 있다. (Other: ‘필드’나 ‘메서드’ 사용)
  • Enum: 한 번 생성되면 메모리에 올라간 상태로 유지된다. (Other: 매번 새로운 메모리 공간 할당)
  • Enum: switch 문에서 사용할 수 있다. (Other: 불가능)
  • Enum: 다른 클래스를 상속할 수 없다. (Other: 가능)