on
아이템 34. int 상수 대신 열거 타입을 사용하라
아이템 34. int 상수 대신 열거 타입을 사용하라
열거 타입은 일정 개수의 상수 값을 정의한 다음, 그 외의 값은 허용하지 않는 타입 입니다.
// 가장 단순한 열거 타입 public enum Apple { FUJI, PIPPIN, GRANNY_SMITH } public enum Orange { NAVEL, TEMPLE, BLOOD }
자바의 열거 타입은 다른 언어의 열거 타입과 비슷한 형태이지만,
완전한 형태의 클래스라서 다른 언어의 열거 타입보다 훨씬 강력합니다.
1. 자바 열거 타입의 아이디어
열거 타입 자체는 클래스이다.
상수 하나당 인스턴스를 하나씩 만들어 public static final 필드로 공개한다.
밖에서 접근할 수 있는 생성자를 제공하지 않으므로 final이다. 따라서 클라이언트가 인스턴스를 직접 생성/확장 할 수 없어 한개만 생성됨이 보장된다.
컴파일타임 타입 안전성을 제공한다.
열거 타입을 매개변수로 받는 메서드를 선언하면, 건네 받은 참조는 열거 타입의 인스턴스임이 확실하다. 열거 타입의 상수를 제거하면, 클라이언트에 아무 영향이 없다. 다시 컴파일하면, 제거된 상수를 참조하는 줄에서 유용한 컴파일 오류가 발생 다시 컴파일하지 않으면, 같은 줄에서 유용한 런타임 오류가 발생
2. 열거 타입에 메서드나 필드 추가하기
열거타입에는 메서드나 필드를 추가할 수 있고, 임의의 인터페이스를 구현하게 할 수도 있다.
// 태양계의 행성 // 데이터와 메서드를 갖는 열거 타입 public enum Planet { MERCURY (3.30, 2.43), VENUS (4.86, 6.05), EARTH (5.97, 6.37), MARS (6.41, 3.39); private final double mass; // 질량 private final double radius; // 반지름 private final double surfaceGravity; // 표면중력 private static final double G = 6.67; // 생성자 Planet(double mass, double radius) { this.mass = mass; this.radu=ius = radius; surfaceGravity = G * mass / (radius * radius); } public double mass() { return mass; } public double radius() { return radius; } public double surfaceGravity() { return surfaceGravity; } public double surfaceWeight(double mass) { return mass * surfaceGravity; }
열거 타입 상수 각각을 특정 데이터와 연결지으려면 생성자에서 데이터를 받아 인스턴스 필드에 저장하면 된다.
열거 타입은 자신 안에 정의된 상수들의 값을 배열에 담아 반환하는 정적 메서드인 values를 제공한다.
값들은 선언된 순서로 저장된다.
public class WeightTable { public static void main(String[] args){ double earthWeight = Double.parseDouble(args[0]); double mass = earthWeight / Planet.EARTH.surfaceGravity(); for (Planet p : Planet.values)) System.out.println("%s에서의 무게는 %f이다.", p, p.surfaceWeight(mass)); } }
3. 열거 타입의 범위
열거 타입은 불변이라 모든 필드는 final이어야 하고,
public 으로 선언해도 되지만, private으로 두고 별도의 public 접근자 메서드를 두는게 낫다.
열거 타입을 선언한 클래스 혹은 패키지에서만 유형한 기능은 private이나 package-private 메서드로 구현한다.
널리 쓰이는 열거 타입은 톱레벨 클래스로 만들고, 특정 톱레벨 클래스에서만 쓰인다면 해당 클래스의 멤버 클래스로 만든다.
4. 상수별 메서드 구현
열거 타입의 상수마다 동작이 달라져야 하는 상황도 있을 것 이다.
사칙 연산 계산기의 연산 종류를 열거타입으로 선언하고,
실제 연산까지 열거 타입 상수가 직접 수행했으면 한다고 하자.
switch를 이용해 분기하는 방법과 상수별 메서드 구현 방법을 비교하자.
// 값에 따라 분기하는 열거 타입 // - 상수가 추가되면 코드도 추가되어야 하는데 // - 깜빡하면 런타임 오류를 낸다 public enum Operation { PLUS, MINUS, TIMES, DIVIDE; public double apply(double x, double y) { swith(this) { case PLUS: return x+y; case MINUS: return x-y; case TIMES: return x*y; case DIVIDE: return x/y; } throw new AssertionError("알 수 없는 연산: " + this); } }
열거 타입에 추상 메서드를 선언하고, 각 상수에서 재정의 하는 방법을 상수별 메서드 구현 이라고 한다.
apply가 추상메서드 이므로 새로운 상수를 추가할 때 재정의 하지 않으면 컴파일 오류로 알려준다.
상수별 메서드 구현을 상수별 데이터와 결합할 수도 있다.
// 상수별 클래스 몸체와 데이터를 사용한 열거타입 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}}, TIMES("*") {public double apply(double x, double y) {return x*y}}, DIVIDE("/") {public double apply(double x, double y) {return x/y}}; public abstract double apply(double x, double y); }
// 상수별 메서드 구현을 활용한 열거타입 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}}, TIMES {public double apply(double x, double y) {return x*y}}, DIVIDE {public double apply(double x, double y) {return x/y}}; public abstract double apply(double x, double y); }
from http://makemoneyamjay.tistory.com/19 by ccl(A) rewrite - 2021-10-04 13:28:01