item 38 junghyunlyoo - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

μ—΄κ±° νƒ€μž… ν™•μž₯을 μ§€μ–‘ν•˜μž

μ—΄κ±° νƒ€μž…μ€ 거의 λͺ¨λ“  μƒν™©μ—μ„œ νƒ€μž… μ•ˆμ „ μ—΄κ±° νŒ¨ν„΄λ³΄λ‹€ μš°μˆ˜ν•˜λ‹€. 단, μ˜ˆμ™Έκ°€ μžˆλ‹€.

νƒ€μž… μ•ˆμ „ μ—΄κ±° νŒ¨ν„΄μ€ ν™•μž₯ν•  수 μžˆμ§€λ§Œ μ—΄κ±° νƒ€μž…μ€ 그럴 수 μ—†λ‹€.

달리 λ§ν•˜λ©΄, νƒ€μž… μ•ˆμ „ μ—΄κ±° νŒ¨ν„΄μ€ μ—΄κ±°ν•œ 값듀을 κ·ΈλŒ€λ‘œ κ°€μ Έμ˜¨ λ‹€μŒ

값을 더 μΆ”κ°€ν•˜μ—¬ λ‹€λ₯Έ λͺ©μ μœΌλ‘œ μ“Έ 수 μžˆμ§€λ§Œ μ—΄κ±° νƒ€μž…μ€ 그럴 수 μ—†λ‹€.

μ‹€μˆ˜λ‘œ μ΄λ ‡κ²Œ λ§Œλ“€μ–΄μ§„ 것은 μ•„λ‹ˆλ‹€!

사싀 μ›¬λ§Œν•˜λ©΄ μ—΄κ±° νƒ€μž…μ„ ν™•μž₯을 지양해야 ν•œλ‹€.

ν™•μž₯ν•œ νƒ€μž…μ˜ μ›μ†Œλ₯Ό 기반 νƒ€μž…μ˜ μ›μ†Œλ‘œ μ·¨κΈ‰ν•œλ‹€λ©΄ κ·Έ λ°˜λŒ€λ„ 성립해야 ν•˜λŠ”λ°, μ—΄κ±° νƒ€μž…μ€ 그렇지 μ•Šλ‹€.

λ”°λΌμ„œ 기반 νƒ€μž…κ³Ό ν™•μž₯된 νƒ€μž…λ“€μ˜ μ›μ†Œ λͺ¨λ‘λ₯Ό μˆœνšŒν•  방법도 λ§ˆλ•…μΉ˜ μ•Šλ‹€.

ν•˜μ§€λ§Œ νŠΉλ³„ν•œ 상황에선 μ—΄κ±° νƒ€μž…μ„ ν™•μž₯ν•˜μž

그런데 ν™•μž₯ν•  수 μžˆλŠ” μ—΄κ±° νƒ€μž…μ΄ μ–΄μšΈλ¦¬λŠ” μ“°μž„μ΄ μ΅œμ†Œν•œ ν•˜λ‚˜λŠ” μžˆλ‹€. λ°”λ‘œ μ—°μ‚° μ½”λ“œλ‹€.

μ—°μ‚° μ½”λ“œμ˜ 각 μ›μ†ŒλŠ” νŠΉμ • 기계가 μˆ˜ν–‰ν•˜λŠ” 연산을 λœ»ν•œλ‹€.(item 34)


μ—΄κ±° νƒ€μž…μœΌλ‘œ 이 효과λ₯Ό λ‚΄λŠ” 방법이 μžˆλ‹€.

μ—΄κ±° νƒ€μž…μ΄ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•  수 μžˆλ‹€λŠ” 사싀을 μ΄μš©ν•˜λŠ” 것이닀.

μ—°μ‚° μ½”λ“œμš© μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•˜κ³  μ—΄κ±° νƒ€μž…μ΄ 이 μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜κ²Œ ν•˜λ©΄ λœλ‹€.

μ΄λ•Œ μ—΄κ±° νƒ€μž…μ΄ κ·Έ μΈν„°νŽ˜μ΄μŠ€μ˜ ν‘œμ€€ κ΅¬ν˜„μ²΄ 역할을 ν•œλ‹€!

public interface Operation {
    double apply(double x, double y);
}
public enum BasicOperation implements 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;

    BasicOperation(String symbol) {
        this.symbol = symbol;
    }

    @Override public String toString() {
        return symbol;
    }
}

μ—΄κ±° νƒ€μž…μΈ BasicOperation은 ν™•μž₯ν•  수 μ—†μ§€λ§Œ μΈν„°νŽ˜μ΄μŠ€μΈ Operation은 ν™•μž₯ν•  수 μžˆλ‹€.

그리고 이 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ—°μ‚°μ˜ νƒ€μž…μœΌλ‘œ μ‚¬μš©ν•˜λ©΄ λœλ‹€.

μ΄λ ‡κ²Œ ν•˜λ©΄ Operation을 κ΅¬ν˜„ν•œ 또 λ‹€λ₯Έ μ—΄κ±° νƒ€μž…μ„ μ •μ˜ν•΄μ„œ κΈ°λ³Έ νƒ€μž…μΈ BasicOperation을 λŒ€μ²΄ν•  수 μžˆλ‹€.

예λ₯Ό λ“€μ–΄ μ§€μˆ˜ μ—°μ‚°(EXP)κ³Ό λ‚˜λ¨Έμ§€ 연산을 μΆ”κ°€ν•΄ 보자.

public enum ExtendedOperation implements Operation {
    EXP("^") {
        public double apply(double x, double y) {
            return Math.pow(x, y);
        }
    },
    REMAINDER("%") {
        public double apply(double x, double y) {
            return x % y;
        }
    };
    private final String symbol;
    ExtendedOperation(String symbol) {
        this.symbol = symbol;
    }
    @Override public String toString() {
        return symbol;
    }
}

μƒˆλ‘œ μž‘μ„±ν•œ 이 연산은 κΈ°μ‘΄ 연산을 μ“°λ˜ 곳이면 μ–΄λ””λ“  μ“Έ 수 μžˆλ‹€.

Operation μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λ„λ‘ μž‘μ„±λ˜μ–΄ 있기만 ν•˜λ©΄ λœλ‹€.

applyκ°€ μΈν„°νŽ˜μ΄μŠ€μ— μ„ μ–Έλ˜μ–΄ μžˆμœΌλ‹ˆ μ—΄κ±° νƒ€μž…μ— λ”°λ‘œ 좔상 λ©”μ„œλ“œλ‘œ μ„ μ–Έν•˜μ§€ μ•Šμ•„λ„ λœλ‹€.


κ°œλ³„ μΈμŠ€ν„΄μŠ€ μˆ˜μ€€μ—μ„œλΏ μ•„λ‹ˆλΌ νƒ€μž… μˆ˜μ€€μ—μ„œλ„ κΈ°λ³Έ μ—΄κ±° νƒ€μž… λŒ€μ‹  ν™•μž₯된 μ—΄κ±° νƒ€μž…μ„ λ„˜κ²¨μ„œ

ν™•μž₯된 μ—΄κ±° νƒ€μž…μ˜ μ›μ†Œ λͺ¨λ‘λ₯Ό μ‚¬μš©ν•˜κ²Œ ν•  μˆ˜λ„ μžˆλ‹€. μ•„λž˜ μ½”λ“œλ₯Ό 보자.

public static void main(String[] args) {
    double x = Double.parseDouble(args[0]);
    double y = Double.parseDouble(args[1]);
    test(ExtendedOperation.class, x, y);
}
private static <T extends Enum<T> & Operation> void test(Class<T> opEnumType, double x, double y) {
    for (Operation op : opEnumType.getEnumConstants())
        System.out.printf("%f %s %f = %f%n",
                x, op, y, op.apply(x, y));
}

main λ©”μ„œλ“œλŠ” test λ©”μ„œλ“œμ— ExtendedOperation의 class λ¦¬ν„°λŸ΄μ„ λ„˜κ²¨μ„œ ν™•μž₯된 연산듀이 무엇인지 μ•Œλ €μ€€λ‹€.

(μ—¬κΈ°μ„œ class λ¦¬ν„°λŸ΄μ€ ν•œμ •μ  νƒ€μž… 토큰 item 33 역할을 ν•œλ‹€)

private static <T extends Enum<T> & Operation> void test(Class<T> opEnumType, double x, double y)

μœ„ λ§€κ°œλ³€μˆ˜ νƒ€μž…μ€ class 객체가 μ—΄κ±° νƒ€μž…μΈ λ™μ‹œμ— Operation의 ν•˜μœ„ νƒ€μž…μ΄μ–΄μ•Ό ν•œλ‹€λŠ” λœ»μ΄λ‹€.

μ—΄κ±° νƒ€μž…μ΄μ–΄μ•Ό μ›μ†Œλ₯Ό μˆœνšŒν•  수 있고 Operation이어야 μ›μ†Œκ°€ λœ»ν•˜λŠ” μˆ˜ν–‰ν•  수 있기 λ•Œλ¬Έμ΄λ‹€.


λ‹€λ₯Έ λ°©λ²•μœΌλ‘œλ„ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ enum을 μ‚¬μš©ν•  수 μžˆλ‹€.

λ°”λ‘œ class 객체 λŒ€μ‹  ν•œμ •μ  μ™€μΌλ“œ μΉ΄λ“œ νƒ€μž…(item 31)인 Collection<? extends Operation>을 λ„˜κΈ°λŠ” 방법이닀.

public static void main(String[] args) {
    double x = Double.parseDouble(args[0]);
    double y = Double.parseDouble(args[1]);
    test(Arrays.asList(ExtendedOperation.values()), x, y);
}
private static void test(Collection<? extends Operation> opSet,
                         double x, double y) {
    for (Operation op : opSet)
        System.out.printf("%f %s %f = %f%n",
                x, op, y, op.apply(x, y));
}

이 μ½”λ“œλŠ” 쑰금 덜 λ³΅μž‘ν•˜λ‹€. 그리고 test λ©”μ„œλ“œκ°€ 쑰금 더 μœ μ—°ν•΄μ‘Œλ‹€.

즉, μ—¬λŸ¬ κ΅¬ν˜„ νƒ€μž…μ˜ 연산을 μ‘°ν•©ν•΄ ν˜ΈμΆœν•  수 있게 λ˜μ—ˆλ‹€.

반면 νŠΉμ • μ—°μ‚°μ—μ„œλŠ” EnumSet(item 36)κ³Ό EnumMap(item 37)을 μ‚¬μš©ν•˜μ§€ λͺ»ν•œλ‹€.

μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ΄μš©ν•œ ν™•μž₯ enum의 단점

μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ΄μš©ν•΄ ν™•μž₯ κ°€λŠ₯ν•œ μ—΄κ±° νƒ€μž…μ„ 흉내 λ‚΄λŠ” 방식에도 ν•œ 가지 μ‚¬μ†Œν•œ λ¬Έμ œκ°€ μžˆλ‹€.

λ°”λ‘œ μ—΄κ±° νƒ€μž…λΌλ¦¬ κ΅¬ν˜„μ„ 상속할 수 μ—†λ‹€λŠ” 점이닀.

아무 μƒνƒœμ—λ„ μ˜μ‘΄ν•˜μ§€ μ•ŠλŠ” κ²½μš°μ—λŠ” λ””ν΄νŠΈ κ΅¬ν˜„(item 20)을 μ΄μš©ν•΄ μΈν„°νŽ˜μ΄μŠ€μ— μΆ”κ°€ν•˜λŠ” 방법이 μžˆλ‹€.

반면 μœ„μ˜ Operation μΈν„°νŽ˜μ΄μŠ€λŠ” μ—°μ‚° 기호λ₯Ό μ €μž₯ν•˜κ³  μ°ΎλŠ” 둜직이 BasicOperationκ³Ό ExtendedOperation λͺ¨λ‘μ— λ“€μ–΄κ°€μ•Όλ§Œ ν•œλ‹€.

사싀 이 κ²½μš°λŠ” μ€‘λ³΅λ˜λŠ” μ½”λ“œκ°€ μ μ§€λ§Œ, λ§Œμ•½ κ³΅μœ ν•˜λŠ” κΈ°λŠ₯이 λ§Žλ‹€λ©΄

κ·Έ 뢀뢄을 λ³„λ„μ˜ λ„μš°λ―Έ ν΄λž˜μŠ€λ‚˜ 정적 λ„μš°λ―Έ λ©”μ„œλ“œλ‘œ λΆ„λ¦¬ν•˜λŠ” λ°©μ‹μœΌλ‘œ μ½”λ“œ 쀑볡을 μ—†μ• μ•Ό ν•œλ‹€.