Item 17. Minimize Mutability

λ³€κ²½ κ°€λŠ₯성을 μ΅œμ†Œν™”ν•˜λΌ

λΆˆλ³€ μΈμŠ€ν„΄μŠ€λ‘œ μƒμ„±λœ μ •λ³΄λŠ” 객체가 μ†Œλ©Έν•˜λŠ” μˆœκ°„κΉŒμ§€ μ ˆλŒ€ 달라지지 μ•ŠλŠ”λ‹€. 이 νŠΉμ§•μ€ κ°€λ³€ ν΄λž˜μŠ€λ³΄λ‹€ μ„€κ³„ν•˜κ³  κ΅¬ν˜„ν•˜κΈ° 쉽고, 였λ₯˜κ°€ 생길 여지도 적고, 훨씬 μ•ˆμ „ν•˜κ²Œ ν•΄μ€€λ‹€.

λΆˆλ³€ 클래슀 μƒμ„±μ˜ 5κ°€μ§€ κ·œμΉ™

κ°„λ‹¨νžˆ λ§ν•˜λ©΄ μœ„μ— μ„€λͺ…ν•œ 것과 같이 μƒμ„±λœ μ‹œμ λΆ€ν„° μ†Œλ©Έλ  λ•ŒκΉŒμ§€ μ ˆλŒ€ 달라지지 μ•Šλ„λ‘ ν•˜λ©΄ λœλ‹€.

  1. 객체의 μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” λ©”μ„œλ“œ(λ³€κ²½μž) 제곡 X

  2. 클래슀 ν™•μž₯ κΈˆμ§€

    • κ°€μž₯ μ‰¬μš΄ 방법은 클래슀λ₯Ό final둜 μ„ μ–Έν•˜μ—¬ ν•˜μœ„ 클래슀 생성을 λ§‰λŠ” 것이 쑴재

    • λͺ¨λ“  μƒμ„±μžλ₯Ό private ν˜Ήμ€ package-private으둜 μ„ μ–Έν•œ λ’€, public 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜λŠ” 방법도 쑴재(더 μœ μ—°ν•œ 방법)

  3. λͺ¨λ“  ν•„λ“œ final μ„ μ–Έ

  4. λͺ¨λ“  ν•„λ“œ private μ„ μ–Έ

  5. μžμ‹  μ™Έ λ‚΄λΆ€μ˜ κ°€λ³€ μ»΄ν¬λ„ŒνŠΈμ— λŒ€ν•œ μ ‘κ·Ό 제곡 X

    • ν΄λΌμ΄μ–ΈνŠΈκ°€ μ œκ³΅ν•œ 객체 μ°Έμ‘° λ°˜ν™˜ X

    • μ ‘κ·Όμž λ©”μ„œλ“œκ°€ κ°€λ³€ 객체 μ°Έμ‘° 직접 λ°˜ν™˜ X

    • μƒμ„±μž / μ ‘κ·Όμž / readObject λ©”μ„œλ“œμ—μ„œ κ°€λ³€ 객체 μ°Έμ‘°λ₯Ό λ°©μ–΄μ μœΌλ‘œ 볡사λ₯Ό μˆ˜ν–‰ν•΄μ•Ό 함

μœ„μ˜ κ·œμΉ™μ„ 지킨 μ˜ˆμ‹œ μ½”λ“œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

public final class Complex { // 2. final μ„ μ–Έν•˜μ—¬ ν•˜μœ„ 클래슀 생성 κΈˆμ§€

    // 3, 4. λͺ¨λ“  ν•„λ“œ final / private μ„ μ–Έ
    private final double re;
    private final double im;

    public Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }

    // 1. 객체의 μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” λ©”μ„œλ“œ(λ³€κ²½μž) 제곡 X
    public double realPart() {
        return re;
    }

    public double imaginaryPart() {
        return im;
    }

    public Complex plus(Complex c) {
        return new Complex(re + c.re, im + c.im); // 5. μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜μ—¬ μ›λž˜ μΈμŠ€ν„΄μŠ€μ˜ 값을 λ³€κ²½ν•˜μ§€ μ•ŠμŒ, μ•„λž˜ λ©”μ„œλ“œλ“€λ„ 동일
    }

    public Complex minus(Complex c) {
        return new Complex(re - c.re, im - c.im);
    }

    public Complex times(Complex c) {
        return new Complex(re * c.re - im * c.im, re * c.im + im * c.re);
    }

    public Complex dividedBy(Complex c) {
        double tmp = c.re * c.re + c.im * c.im;
        return new Complex((re * c.re + im * c.im) / tmp, (im * c.re - re * c.im) / tmp);
    }

    // equals / hashCode / toString ...
}

μ—¬κΈ°μ„œ 사칙 μ—°μ‚° λ©”μ„œλ“œ(plus, minus, times, dividedBy)λŠ” λͺ¨λ‘ μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜μ—¬ μ›λž˜ μΈμŠ€ν„΄μŠ€μ˜ 값을 λ³€κ²½ν•˜μ§€ μ•Šκ³ , μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€. λ•Œλ¬Έμ— λ©”μ„œλ“œ 이름도 add같은 동사가 μ•„λ‹Œ plus같은 μ „μΉ˜μ‚¬λ₯Ό μ‚¬μš©ν•œ 것도 μ•Œ 수 μžˆλ‹€.(BigInteger, BigDecimal ν΄λž˜μŠ€λŠ” ν•΄λ‹Ή λͺ…λͺ… κ·œμΉ™μ„ μ§€ν‚€μ§€ μ•Šμ•„ ν˜Όλž€μ„ μ£Όκ³  μžˆλ‹€.)

λΆˆλ³€ 클래슀의 μž₯점

μŠ€λ ˆλ“œ μ•ˆμ „κ³Ό μΈμŠ€ν„΄μŠ€ 캐싱

근본적으둜 λ³€ν•˜μ§€ μ•Šμ•„ μŠ€λ ˆλ“œ μ•ˆμ „ν•˜κΈ° λ•Œλ¬Έμ— λ”°λ‘œ 동기화할 ν•„μš”κ°€ μ—†μ–΄ μ—¬λŸ¬ μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— μ‚¬μš©ν•΄λ„ μ•ˆμ „ν•˜λ‹€. λΆˆλ³€ 객체둜 λ§Œλ“€μ–΄ λ†“λŠ” 것이 μŠ€λ ˆλ“œ μ•ˆμ „μ„±μ„ 얻을 수 μžˆλŠ” κ°€μž₯ μ‰¬μš΄ 방법이기도 ν•˜λ‹€.

λΆˆλ³€κ°μ²΄λŠ” λ‹€λ₯Έ μŠ€λ ˆλ“œμ— 영ν–₯을 μ£Όμ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μ΄λŸ¬ν•œ μž₯점을 μ‚΄λ € ν•œ 번 λ§Œλ“  μΈμŠ€ν„΄μŠ€λ₯Ό 쀑볡 μƒμ„±ν•˜μ§€ μ•Šκ³  μž¬ν™œμš©ν•˜λŠ” 것이 κ°€λŠ₯ν•˜λ‹€. μ•„λž˜ 두 κ°€μ§€ λ°©λ²•μœΌλ‘œ μ œκ³΅ν•΄μ€„ 수 μžˆλŠ”λ°, 2λ²ˆμ„ μ‚¬μš©ν•˜κ²Œ 되면 ν΄λΌμ΄μ–ΈνŠΈλ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šκ³  ν•„μš”μ— 따라 λΆˆλ³€ 객체λ₯Ό μΊμ‹±ν•˜μ—¬ μž¬ν™œμš©ν•  수 μžˆλŠ” μž₯점이 생긴닀.

// 1. public static final ν•„λ“œλ‘œ λΆˆλ³€ 객체 생성
public final class Complex {
    public static final Complex ZERO = new Complex(0, 0);
    public static final Complex ONE = new Complex(1, 0);
    public static final Complex I = new Complex(0, 1);
    // ...
}

// 2. 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ‘œ λΆˆλ³€ 객체 생성
public final class Integer extends Number implements Comparable<Integer> {
    // ...

    @HotSpotIntrinsicCandidate
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    // ...
}

λ‚΄λΆ€ 데이터 곡유

λΆˆλ³€ κ°μ²΄λΌλ¦¬λŠ” λ‚΄λΆ€ 데이터λ₯Ό κ³΅μœ ν•˜μ—¬ λ³΅μ‚¬ν•˜μ§€ μ•Šκ³  μ‚¬μš©ν•  수 μžˆλ‹€.

public class BigInteger extends Number implements Comparable<BigInteger> {

    final int signum; // λΆ€ν˜Έ
    final int[] mag; // μ ˆλŒ“κ°’, 배열이기 λ•Œλ¬Έμ— κ°€λ³€

    // ...

    public BigInteger negate() {
        return new BigInteger(this.mag, -this.signum); // λ³΅μ‚¬ν•˜μ§€ μ•Šκ³  κ·ΈλŒ€λ‘œ μ‚¬μš©ν•˜μ—¬, μƒˆλ‘œ μƒμ„±λœ μΈμŠ€ν„΄μŠ€μ™€ λ‚΄λΆ€ 배열을 곡유 
    }
}

λΆˆλ³€ 객체이기 λ•Œλ¬Έμ— κ°€λ³€ 객체인 λ‚΄λΆ€ 배열이 λ³€κ²½λ˜μ§€ μ•ŠλŠ”λ‹€λŠ” 것을 보μž₯ν•  수 있기 λ•Œλ¬Έμ— κ°€λŠ₯ν•œ 방법이닀.

μ‹€νŒ¨ μ›μžμ„± 제곡

λ‚΄λΆ€ μƒνƒœλ₯Ό λ°”κΎΈμ§€ μ•ŠκΈ° λ•Œλ¬Έμ— λ©”μ„œλ“œμ—μ„œ μ˜ˆμ™Έκ°€ λ°œμƒν•œ 후에도 κ·Έ κ°μ²΄λŠ” μ—¬μ „νžˆ μœ νš¨ν•œ μƒνƒœμ—¬μ•Ό ν•œλ‹€(μ‹€νŒ¨ μ›μžμ„±)λŠ” κ·œμΉ™μ„ μžμ—°μŠ€λŸ½κ²Œ λ”°λ₯΄κ²Œ λœλ‹€.

λΆˆλ³€ 클래슀의 단점

μž₯점이자 단점인 λΆ€λΆ„μœΌλ‘œ, 값을 μ•„μ£Ό 쑰금 λ³€κ²½ν•˜κΈ° μœ„ν•΄μ„œλ„ μƒˆλ‘œμš΄ 객체λ₯Ό 생성해야 ν•œλ‹€λŠ” 점이닀. 이 문제λ₯Ό λŒ€μ²˜ν•˜κΈ° μœ„ν•΄, κ°€λ³€ λ™λ°˜ 클래슀λ₯Ό λ§Œλ“€μ–΄ λΆˆλ³€ 클래슀의 단점이 μ‘΄μž¬ν•˜λ‚˜ μ—°μ‚°λ§ˆλ‹€ 객체λ₯Ό μƒμ„±ν•˜μ§€ μ•ŠλŠ” κ°€λ³€ λ™λ°˜ 클래슀둜 ν•΄κ²°ν•  수 μžˆλ‹€.

  • ν΄λΌμ΄μ–ΈνŠΈκ°€ μ‚¬μš©ν•  연산을 예츑 κ°€λŠ₯ν•œ 경우: package-private에 κ°€λ³€ λ™λ°˜ 클래슀λ₯Ό 두어 연산을 μˆ˜ν–‰(BigInteger)

  • ν΄λΌμ΄μ–ΈνŠΈκ°€ μ‚¬μš©ν•  연산을 μ˜ˆμΈ‘ν•  수 μ—†λŠ” 경우: StringBuilder와 같은 λ³„λ„μ˜ κ°€λ³€ λ™λ°˜ 클래슀λ₯Ό 제곡

Last updated

Was this helpful?