이펙티브 자바 아이템 34. int 상수 대신 열거 타입을 사용하라. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.github.sejoung.codetest.enumtest;public class Constants { 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 ; }
위에 정수 열거 패턴에는 단점들이 많다. 실제로 오렌지와 사과의 이름이 동일한것이 있으면 같은것을 구분하기 위해 앞에 명칭에 오렌지를 붙혀야 했다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.github.sejoung.codetest.enumtest;public class OldTest { public static void main (String[] args) { OldTest ot = new OldTest (); ot.orangeShow(Constants.ORANGE_BLOOD,Constants.ORANGE_NAVEL,Constants.APPLE_FUJI); } public void orangeShow (int ...orangeType) { for (int orange : orangeType){ System.out.println(orange); } } }
실행결과
1 2 3 4 5 2 0 0 Process finished with exit code 0
그리고 위에 코드 처럼 오렌지를 보내야 하는 클래스에 사과를 보내도 문제가 생기지 않는다.
문자열 상수 패턴은 더 나쁘다. 문자열 상수 패턴을 쓰면 컴파일 시점에 해당 상수가 String으로 변환되어 수정하려고 하면 컴파일을 다시 해야된다.
이 모든 문제를 해결해주는 것이 열거 타입이다.
1 2 3 4 5 6 7 8 package com.github.sejoung.codetest.enumtest;public enum Orange { NAVEL, TEMPLE, BLOOD }
1 2 3 4 5 6 7 8 package com.github.sejoung.codetest.enumtest;public enum Apple { FUJI, PIPPIN, GRANNY_SMITH }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.github.sejoung.codetest.enumtest;public class EnumTest { public static void main (String[] args) { EnumTest et = new EnumTest (); et.orangeShow(Orange.BLOOD,Orange.TEMPLE); } public void orangeShow (Orange... orangeType) { for (Orange orange : orangeType){ System.out.println(orange); } } }
이전에 만들었던 파일을 enum으로 바꾸면 오렌지를 사용하는곳에 사과가 들어갈수가 없다.
데이터와 메서드를 갖는 열거타입도 만들수 있다. 열거타입은 불변이라 모든필드가 final이여야 한다
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package com.github.sejoung.codetest.enumtest;public enum Planet { MERCURY(3.302e+23 , 2.439e6 ), VENUS (4.869e+24 , 6.052e6 ), EARTH (5.975e+24 , 6.378e6 ), MARS (6.419e+23 , 3.393e6 ), JUPITER(1.899e+27 , 7.149e7 ), SATURN (5.685e+26 , 6.027e7 ), URANUS (8.683e+25 , 2.556e7 ), NEPTUNE(1.024e+26 , 2.477e7 ); private final double mass; private final double radius; private final double surfaceGravity; private static final double G = 6.67300E-11 ; Planet(double mass, double radius) { this .mass = mass; this .radius = 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; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.github.sejoung.codetest.enumtest;public class WeightTable { public static void main (String[] args) { double earthWeight = 185 ; double mass = earthWeight / Planet.EARTH.surfaceGravity(); for (Planet p : Planet.values()) System.out.printf("%s에서의 무게는 %f이다.%n" , p, p.surfaceWeight(mass)); } }
실행결과
1 2 3 4 5 6 7 8 9 10 11 12 MERCURY에서의 무게는 69.912739이다. VENUS에서의 무게는 167.434436이다. EARTH에서의 무게는 185.000000이다. MARS에서의 무게는 70.226739이다. JUPITER에서의 무게는 467.990696이다. SATURN에서의 무게는 197.120111이다. URANUS에서의 무게는 167.398264이다. NEPTUNE에서의 무게는 210.208751이다. Process finished with exit code 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 package com.github.sejoung.codetest.enumtest;import java.util.Map;import java.util.Optional;import java.util.stream.Stream;import static java.util.stream.Collectors.toMap;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; } }; private final String symbol; Operation(String symbol) { this .symbol = symbol; } @Override public String toString () { return symbol; } public abstract double apply (double x, double y) ; private static final Map<String, Operation> stringToEnum = Stream.of(values()).collect( toMap(Object::toString, e -> e)); public static Optional<Operation> fromString (String symbol) { return Optional.ofNullable(stringToEnum.get(symbol)); } public static void main (String[] args) { double x = 2 ; double y = 4 ; for (Operation op : Operation.values()) { System.out.printf("%f %s %f = %f%n" , x, op, y, op.apply(x, y)); } Operation.fromString("+" ).ifPresent((s)->{ System.out.println(s); System.out.println(s.name()); }); } }
실행결과
1 2 3 4 5 6 7 2.000000 + 4.000000 = 6.000000 2.000000 - 4.000000 = -2.000000 2.000000 * 4.000000 = 8.000000 2.000000 / 4.000000 = 0.500000 + PLUS
전략 열거 타입 패턴
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 package com.github.sejoung.codetest.enumtest;import static com.github.sejoung.codetest.enumtest.PayrollDay.PayType.*;enum PayrollDay { MONDAY(WEEKDAY), TUESDAY(WEEKDAY), WEDNESDAY(WEEKDAY), THURSDAY(WEEKDAY), FRIDAY(WEEKDAY), SATURDAY(WEEKEND), SUNDAY(WEEKEND); private final PayType payType; PayrollDay(PayType payType) { this .payType = payType; } int pay (int minutesWorked, int payRate) { return payType.pay(minutesWorked, payRate); } enum PayType { WEEKDAY { int overtimePay (int minsWorked, int payRate) { return minsWorked <= MINS_PER_SHIFT ? 0 : (minsWorked - MINS_PER_SHIFT) * payRate / 2 ; } }, WEEKEND { int overtimePay (int minsWorked, int payRate) { return minsWorked * payRate / 2 ; } }; abstract int overtimePay (int mins, int payRate) ; private static final int MINS_PER_SHIFT = 8 * 60 ; int pay (int minsWorked, int payRate) { int basePay = minsWorked * payRate; return basePay + overtimePay(minsWorked, payRate); } } public static void main (String[] args) { for (PayrollDay day : values()) System.out.printf("%-10s%d%n" , day, day.pay(8 * 60 , 1 )); } }
실행결과
1 2 3 4 5 6 7 8 9 10 MONDAY 480 TUESDAY 480 WEDNESDAY 480 THURSDAY 480 FRIDAY 480 SATURDAY 720 SUNDAY 720 Process finished with exit code 0
switch 문을 이용해 원래 열거 타입에 없는 기능을 수행한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.github.sejoung.codetest.enumtest;public class Inverse { 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 ("Unknown op: " + op); } } public static void main (String[] args) { double x = 2 ; double y = 4 ; for (Operation op : Operation.values()) { Operation invOp = inverse(op); System.out.printf("%f %s %f %s %f = %f%n" , x, op, y, invOp, y, invOp.apply(op.apply(x, y), y)); } } }
실행결과
1 2 3 4 5 6 7 8 2.000000 + 4.000000 - 4.000000 = 2.000000 2.000000 - 4.000000 + 4.000000 = 2.000000 2.000000 * 4.000000 / 4.000000 = 2.000000 2.000000 / 4.000000 * 4.000000 = 2.000000 Process finished with exit code 0
필요한 원소를 컴파일타임에 다 알수 있는 상수 집합이라면 항상 열거 타입을 사용하자
참조