# 아이템34 int 상수 대신 열거 타입을 사용하라

```java
public static final int APPLE_FUJI = 0;
public static final int APPLE_PIPPIN = 1;
public static final int APPLE_GRANNY_SMITH = 2;

public static final int ORANGE_NAVEL = 0;
public static final int ORANGE_TEMPLE = 1;
public static final int ORANGE_BLOOD = 2;
```

#### 정수 열거 패턴 기법에는 단점이 많다.

* `타입 안전`을 보장할 방법이 없으며 `표현력`도 좋지 않다. 오렌지를 건네야 할 메서드에 사과를 보내고 `동등 연산자`로 비교하더라도 컴파일러는 아무런 경고 메시지를 출력하지 않는다.
* 상수의 값이 바뀌면 클라이언트도 반드시 다시 `컴파일`해야 한다. 다시 컴파일하지 않은 클라이언트는 실행이 되더라도 엉뚱하게 동작할 것이다.
* 정수 상수는 `문자열`로 출력하기가 다소 까다롭다. 심지어 그 안에 상수가 몇 개 인지도 알 수 없다.
* 정수 대신 문자열 상수를 사용하는 변형하는 패턴도 있지만, 상수의 의미를 출력할 수 있다는 점은 좋지만 `문자열 상수`의 이름 대신 `문자열` 값을 그대로 사용하면 오타가 있어도 컴파일러는 확인할 길이 없으니 자연스럽게 `런타임 버그`가 생긴다.

#### 열거 타입을 쓰면 어떤 부분이 장점이 있을까

* 열거 타입 자체는 `클래스`이며, 상수 하나당 `자신의 인스턴스`를 하나씩 만들어 `public static final` 필드로 공개한다. 열거 타입은 밖에서 접근할 수 있는 생성자를 제공하지 않으므로 사실상 `final`이다.
* 열거 타입은 `컴파일 타임 타입 안전성`을 제공한다.
* 열거 타입에는 `각자의 이름 공간`이 있어서 이름이 같은 상수도 평화롭게 공존한다.
* 열거 타입에 새로운 상수를 추가하거나 순서를 바꿔도 다시 `컴파일`하지 않아도 된다.
* 열거 타입에는 `임의의 메서드`나 필드를 추가할 수 있고 `임의의 인터페이스`를 구현하게 할 수도 있다.
* 열거 타입은 자신 안에 정의된 상수 들의 값을 배열에 담아 반환하는 정적 메서드인 `values`를 제공한다.

  ```
  public class WeightTable {
  	public static void main(String[] args) {
  		double eartchWeight = Double.parseDouble(args[0]);
  		double mass = earchWeight / Planet.EARTH.surfaceGravity();
  		for (Planet p : Planet.values())
  			System.out.printf("%s에서의 무게는 %f이다.\\n",p,p.surfaceWeight(mass));
  	}
  }
  ```
* 열거 타입은 `상수별 메서드 구현`도 가능하다.

  ```
  public enum Operation {
  	PLUS("+") {
  		public double apply(double x, double y) { return x + y; }
  	}
  	MINUS("-") {
  		public double apply(double x, double y) { return x - y; }
  	...
  }
  ```

  ```
  public static void main(String[] args) {
  	double x = Double.parseDouble(args[0]);
  	double y = Double.parseDouble(args[1]);
  	for (Operation op : Operation.values())
  		System.out.printf("%f %s %f = %f\\n", x, op, y, op.apply(x,y));
  }
  ```
* 하지만 기존 열거 타입에 상수별 동작을 혼합해 넣을 때는 `switch` 문이 좋은 선택이 될 수 있다.

  ```
  public static Operation inverse(Operation op) {
  	switch(op) {
  		case PLUS: return Operation.MINUS;
  		case MINUS: return Operation.PLUS;
  		case TIMES: return Operation.DIVIDE;
  		case DIVIDE: return Operation.TIMES;

  		default: throw new AssertionError("알 수 없는 연산 : "+ op);
  	}
  }
  ```

#### 열거 타입에서 상수를 하나 제거하면 어떻게 되지?

제거한 상수를 참조하지 않는 `클라이언트`에는 아무 영향이 없다. 클라이언트를 다시 `컴파일`하지 않으면 `런타임`에 역시 같은 줄에서 유용한 예외가 발생할 것이다.

![](/files/-MFpI_cgSDXWH27MHzjm)

### 정리

`열거 타입`은 확실히 정수 상수보다 뛰어나다. 더 읽기 쉽고 안전하고 강력하다. 대다수 열거 타입이 명시적 `생성자`나 `메서드` 없이 쓰이지만, 각 상수를 특정 데이터와 연결되거나 상수 마다 다르게 동작하게 할 때는 필요하다. 드물게는 하나의 메서드가 상수별로 다르게 동작해야 할 때도 있다. 이런 열거 타입 에서는 switch 문 대신 상수별 메서드 구현을 사용하자.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://incheol-jung.gitbook.io/docs/study/effective-java/undefined-4/34-int.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
