<aside> <img src="/icons/help-alternate_gray.svg" alt="/icons/help-alternate_gray.svg" width="40px" />

자바에는 특수한 목적의 참조 타입이 두 가지가 있다. 하나는 클래스의 일종인 열거 타입(enum; 열거형)이고, 다른 하나는 인터페이스의 일종인 애너테이션(annotation)이다. 이 타입들을 올바르게 사용하는 방법을 알아보자!

</aside>

34] int 상수 대신 열거 타입을 사용하라

Java에서 열거 타입을 지원하기 전에는 아래와 같이 정수 상수를 한 묶음으로 선언하여 사용하였다. 하지만 이런 '정수 열거 패턴'은 많은 단점들이 존재한다.

public static final int APPLE_PIE = 0;
public static final int APPLE_JAM = 1;

public static final int GRAPE_PIE = 0;
public static final int GRAPE_JAM = 1;
  1. 타입 안전을 보장할 방법이 없으며 표현력이 좋지 않다.

위 코드에서 보는 것과 같이 APPLE_GRAPE_PIE와 GRAPE_PIE는 다른 접두어를 써서 겉보기에는 다른 곳에서 사용할 것을 알 수 있지만 컴파일러 입장에서는 둘다 정수 0이기 때문에 APPLE_PIE가 사용되는 메서드에 GRAPE_PIE가 전달되어도  컴파일때 아무런 문제가 없다.

  1. 정수 열거 패턴을 사용한 프로그램은 깨지기 쉽다.

평범한 상수를 나열한 것 뿐이라 컴파일하면 해당 갑이 클라이언트 파일에 그대로 새겨진다. 만약 상수의 값이 바뀌면 반드시 다시 컴파일 해야 한다.

```java
public static final int APPLE_PIE = 0;

//결국 똑같은 코드!
System.out.println(APPLIE_PIE);
System.out.println(0);
```
  1. 정수 상수는 문자열로 출력하기 어렵다.

    public static final int APPLE_PIE = 0;
    System.out.println(APPLIE_PIE); // APPLIE_PIE 가 아닌 0이 출력!
    
  2. 같은 정수 열거 그룹에 속한 모든 상수를 한 바퀴 순회하는 방법도 마땅치 않다.

이런 정수 열거 패턴의 단점을 보완하고 여러 장점들을 안겨주는 대안이 바로 자바의 열거 타입이다.

- 열거 타입

중요한 것은 열거타입 자체는 클래스이다. 그리고 열거 상수 하나당 자신의 인스턴스를 하나씩 만들어 public static final 필드로 공개한다. 또한 열거 타입은 밖에서 접근할 수 있는 생성자를 제공하지 않는다. 이런 사실들이 열거 타입의 여러 장점을 보여준다.

  1. **열거 타입 선언으로 만들어진 인스턴스들은 딱 하나씩만 존재한다.

클라이언트가 인스턴스를 직접 생성하거나 확장 할 수 없기 때문이다. 싱글톤은 원소가 하나뿐인 열거 타입이라 할 수 있고, 열거 타입은 싱글톤을 일반화한 형태로 볼 수 있다.**

  1. 열거 타입은 컴파일 타임 타입 안정성을 제공한다.

    public enum WeekDay {
        MONDAY(0),
        TUESDAY(1),
        WEDNESDAY(2),
        THURSDAY(3),
        FRIDAY(4),
        SATURDAY(5),
        SUNDAY(6);
    
        private final int value;
    
        WeekDay(int value) {
            this.value = value;
        }
    }
    

    WeekDay 열거타입을 인자로 받으려는 메소드는 반드시 WeekDay의 인스턴스를 건네줘야 한다.

  2. 열거 타입에는 각자의 이름공간이 있어서 이름이 같은 상수도 평화롭게 공존한다.

    private enum Apple{
    		PIE,JAM;
    	}
    	private enum Grape{
    		PIE,JAM;
    	}
        
        //같은 클래스 안에 enum
    

    또한 열거 타입에 새로운 상수를 추가하거나 순서를 바꿔도 다시 컴파일 하지 않아도 된다. 정수 열거 패턴과 달리 상수 값이 클라이언트로 컴파일되어 각인되지 않기 때문이다.

  3. 열거 타입의 toString메소드는 출력하기에 적합한 문자열을 내어준다.

  4. 열거 타입에는 임의의 메서드나 필드를 추가할 수 있고, 임의의 인터페이스를 구현할 수도 있다.

    public enum WeekDay {
        MONDAY(0),
        TUESDAY(1),
        WEDNESDAY(2),
        THURSDAY(3),
        FRIDAY(4),
        SATURDAY(5),
        SUNDAY(6);
    
    	//임의의 필드
        private final int value;
    
        WeekDay(int value) {
            this.value = value;
        }
    
    	//임의의 메소드
        public void printHi() {
            System.out.println("Hi! This is " + this.name());
        }
        
    }
    

    Eumn 자체는 Object메서드들과 Comparable, Serializable을 구현해두었다.

    public abstract class Enum<E extends Enum<E>> implements Comparable<E>,Serializable{
    
    }
    

    열거 타입은 근본적으로 불변이라 모든 필드는 final이어야한다. 열거 타입은 자신 안에 정의된 상수들을 배열에 담아 반환하는 values()메서드를 제공한다. 상수들은 선언된 순서대로 저장된다.

    public enum Planet {
        // 중략....
        
        //재정의한 toString, 이름을 소문자로 출력한다.
        @Override
        public String toString() {
            return this.name().toLowerCase();
        }
    }
    
    //Planet[] planets = Planet.values();