item 17 hyowon - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

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

λΆˆλ³€ν΄λž˜μŠ€λž€?

μΈμŠ€ν„΄μŠ€μ˜ λ‚΄λΆ€ 값을 μˆ˜μ •ν•  수 μ—†λŠ” 클래슀

λ§₯락상 클래슀 상속이 λΆˆκ°€λŠ₯ final classλ₯Ό λ§ν•˜λŠ”κ²ƒ κ°™μ§€λ§Œ, 그보닀 더 μƒμœ„μ˜ κ°œλ…μ΄λ‹€.

final ν΄λž˜μŠ€λŠ” λΆˆλ³€ν΄λž˜μŠ€ κ΅¬ν˜„ 방식 쀑 ν•˜λ‚˜μ΄λ‹€.

λΆˆλ³€ 클래슀의 예

String, κΈ°λ³Έ νƒ€μž…μ˜ λ°•μ‹± 클래슀, BigInteger, BigDecimal

item11의 PhoneNumber ν΄λž˜μŠ€λ„ λΆˆλ³€μ΄λ‹€.

μž₯점

  1. λ‹¨μˆœν•˜λ‹€
    1. μƒμ„±λœ μ‹œμ μ˜ μƒνƒœλ₯Ό 파괴될 λ•ŒκΉŒμ§€ κ·ΈλŒ€λ‘œ κ°„μ§ν•˜λ―€λ‘œ, λ‚΄λΆ€ 객체에 뭐가 λ“€μ—ˆμ„μ§€ λͺ…ν™•νžˆ μ•Œ 수 μžˆλ‹€.
    2. κ°€λ³€ κ°μ²΄λŠ” μž„μ˜μ˜ λ³΅μž‘ν•œ μƒνƒœμ— 놓일 수 μžˆμ–΄, λ³€κ²½μž λ©”μ„œλ“œκ°€ μΌμœΌν‚€λŠ” μƒνƒœ 전이λ₯Ό μ •λ°€ν•˜κ²Œ λ¬Έμ„œλ‘œ 남겨놓지 μ•ŠμœΌλ©΄ λ―Ώκ³  μ‚¬μš©ν•˜κΈ° μ–΄λ ΅λ‹€.
  2. μŠ€λ ˆλ“œ μ•ˆμ „ν•˜μ—¬ λ”°λ‘œ 동기화할 ν•„μš”κ°€ μ—†λ‹€.
    1. μ—¬λŸ¬ μŠ€λ ˆλ“œκ°€ λ™μ‹œμ— μ‚¬μš©ν•΄λ„ μ ˆλŒ€ ν›Όμ†λ˜μ§€ μ•ŠμœΌλ‹ˆ, μ•ˆμ‹¬ν•˜κ³  κ³΅μœ ν•  수 μžˆλ‹€. λ”°λΌμ„œ λΆˆλ³€ ν΄λž˜μŠ€λŠ” ν•œ 번 λ§Œλ“  μΈμŠ€ν„΄μŠ€λ₯Ό μ΅œλŒ€ν•œ μž¬ν™œμš©ν•˜κΈ°λ₯Ό κΆŒν•œλ‹€. κ°€μž₯ μ‰¬μš΄ μ˜ˆλŠ” 값을 μƒμˆ˜(public static final)둜 μ œκ³΅ν•˜λŠ” 것이닀.
    2. 방어적 볡사도 ν•„μš” μ—†λ‹€.
  3. λΆˆλ³€ 객체끼리 λ‚΄λΆ€ 데이터λ₯Ό κ³΅μœ ν•  수 μžˆλ‹€.
    1. BigInteger 클래슀 λ‚΄λΆ€μ—μ„œ κ°’μ˜ λΆ€ν˜Έμ™€ 크기λ₯Ό λ”°λ‘œ ν‘œν˜„ν•œλ‹€. λΆ€ν˜ΈλŠ” int λ³€μˆ˜λ₯Ό, ν¬κΈ°λŠ” int 배열을 μ‚¬μš©ν•œλ‹€. ν•΄λ‹Ή 클래슀 λ‚΄λΆ€μ˜ negate λ©”μ„œλ“œλŠ” 크기가 κ°™κ³  λΆ€ν˜Έλ§Œ λ°˜λŒ€μΈ μƒˆλ‘œμš΄ BigIntegerλ₯Ό μƒμ„±ν•˜λŠ”λ° 이 λ•Œ 원본 μΈμŠ€ν„΄μŠ€μ˜ int 배열을 κ³΅μœ ν•œλ‹€.
  4. 객체λ₯Ό λ§Œλ“€ λ•Œ λ‹€λ₯Έ λΆˆλ³€ 객체듀을 κ΅¬μ„±μš”μ†Œλ‘œ μ‚¬μš©ν•˜λ©΄ λΆˆλ³€μ‹μ„ μœ μ§€ν•˜κΈ° μˆ˜μ›”ν•˜λ‹€.
    1. λΆˆλ³€ κ°μ²΄λŠ” 맡의 킀와 집합(Set)의 μ›μ†Œλ‘œ μ“°κΈ° μ•ˆμ„±λ§žμΆ€μ΄λ‹€. λ§΅μ΄λ‚˜ 집합은 μ•ˆμ— λ‹΄κΈ΄ 값이 λ°”λ€Œλ©΄ λΆˆλ³€μ‹μ΄ ν—ˆλ¬Όμ–΄μ§€λŠ”λ°, λΆˆλ³€ 객체λ₯Ό μ‚¬μš©ν•˜λ©΄ 그럴 일이 μ—†λ‹€.

단점

  1. 값이 λ‹€λ₯΄λ©΄ λ°˜λ“œμ‹œ λ…λ¦½λœ 객체둜 λ§Œλ“€μ–΄μ•Ό ν•œλ‹€.
    1. κ°’μ˜ κ°€μ§“μˆ˜κ°€ 많으면 이듀을 λͺ¨λ‘ λ§Œλ“œλŠ”λ° μ‹œκ°„, κ³΅κ°„μ μœΌλ‘œ 큰 λΉ„μš©μ„ μΉ˜λŸ¬μ•Ό ν•œλ‹€.
    2. λŒ€μ²˜λ²•μœΌλ‘œλŠ” ν”νžˆ 쓰일 닀단계 연산듀을 μ˜ˆμΈ‘ν•˜μ—¬ κΈ°λ³Έ κΈ°λŠ₯으둜 μ œκ³΅ν•˜λŠ” 방법을 μ‚¬μš©ν•˜λ©΄ λ˜λŠ”λ°, 1) ν΄λΌμ΄μ–ΈνŠΈκ°€ μ›ν•˜λŠ” λ³΅μž‘ν•œ 연산을 μ •ν™•νžˆ μ˜ˆμΈ‘ν•  수 μžˆλ‹€λ©΄ package-private의 κ°€λ³€ λ™λ°˜ 클래슀둜 μΆ©λΆ„ν•˜κ³ , 2) 그렇지 μ•Šλ‹€λ©΄ public으둜 클래슀λ₯Ό μ œκ³΅ν•΄μ•Ό ν•œλ‹€.

final ν‚€μ›Œλ“œ

클래슀 μ•žμ—μ„œ ν•΄λ‹Ή 클래슀의 상속을 λ§‰λŠ”λ‹€.

λ©”μ„œλ“œ μ•žμ—μ„œ ν•΄λ‹Ή λ©”μ„œλ“œμ˜ μ˜€λ²„λΌμ΄λ”©, 즉 μžμ‹ν΄λž˜μŠ€μ—μ„œ μž¬μ •μ˜ν•˜λŠ” 것을 λ§‰λŠ”λ‹€.

λΆˆλ³€ 클래슀λ₯Ό λ§Œλ“œλŠ” κ·œμΉ™ - P/F 컴변상

  1. 객체의 μƒνƒœλ₯Ό λ³€κ²½ν•˜λŠ” λ©”μ„œλ“œ(λ³€κ²½μž)λ₯Ό μ œκ³΅ν•˜μ§€ μ•ŠλŠ”λ‹€
  2. 상속 λΆˆκ°€λŠ₯ν•˜κ²Œ λ§Œλ“ λ‹€
    • 상속받아 쒋지 μ•Šμ€ μ˜λ„λ‘œ λ©”μ„œλ“œλ₯Ό λ³€κ²½ν•˜λŠ” 것을 λ§‰λŠ”λ‹€.
    • κ°„λ‹¨ν•˜κ²ŒλŠ” final 클래슀λ₯Ό μ„ μ–Έν•˜λŠ” 방법이 μžˆλ‹€.
    • 쒀더 μœ μ—°ν•œ λ°©λ²•μœΌλ‘œλŠ” λͺ¨λ“  μƒμ„±μžλ₯Ό private ν˜Ήμ€ package-private둜 λ§Œλ“€κ³  public 정적 νŒ©ν„°λ¦¬λ₯Ό μ œκ³΅ν•œλ‹€. (정적 νŒ©ν„°λ¦¬?)
  3. λͺ¨λ“  ν•„λ“œλ₯Ό final둜 μ„ μ–Έν•œλ‹€
  4. λͺ¨λ“  ν•„λ“œλ₯Ό private둜 μ„ μ–Έν•œλ‹€.
    • public final을 μ‚¬μš©ν•΄λ„ λ˜μ§€λ§Œ, κ·Έλ ‡κ²Œ 되면 λ‚΄λΆ€ ν‘œν˜„μ„ λ°”κΏ€ 수 μ—†κ²Œ λ˜λ―€λ‘œ, privateλ₯Ό μ‚¬μš©
  5. μžμ‹  μ™Έμ—λŠ” λ‚΄λΆ€μ˜ κ°€λ³€ μ»΄ν¬λ„ŒνŠΈμ— μ ‘κ·Όν•  수 없도둝 ν•œλ‹€.
    • μ»΄ν¬λ„ŒνŠΈλž€?
    • 각각 λ…λ¦½μ μœΌλ‘œ κΈ°λŠ₯ν•˜λŠ” λͺ¨λ“ˆ
    • μ»΄ν¬λ„ŒνŠΈ λ‚΄λΆ€κ°€ λ“œλŸ¬λ‚˜μ„  μ•ˆλ˜κ³ , 까보렀 ν•΄μ„œλ„ μ•ˆλ¨
    • λ‹€λ§Œ, ν•΄λ‹Ή μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•˜κ²Œ ν•΄μ£ΌλŠ” 'μž₯치'λŠ” ν•„μš”ν•˜λ‹€.
    • ν•˜λ“œμ›¨μ–΄ μͺ½μ—μ„œ μŠ€λ§ˆνŠΈν°μ— 배터리가 λͺ»μ“°κ²Œ 되면 λ‹€λ₯Έ 걸둜 κ°ˆμ•„λ‚„ 수 있음
    • 단, λ°°ν„°λ¦¬λŠ” 슀마트폰 λ‹¨μžμ™€ 배터리 λ‹¨μžμ™€ λ§žλ¬Όλ €μ•Ό μ‚¬μš©κ°€λŠ₯ν•œλ°, 이 λ•Œ 배터리 λ‹¨μžκ°€ 'μž₯치'에 ν•΄λ‹Ήν•œλ‹€.
    • OOP의 class κ°œλ…κ³ΌλŠ” λ‹€λ₯΄λ‹€. μ»΄ν¬λ„ŒνŠΈμ˜ μ •μ˜λ§Œ μΆ©μ‘±ν•œλ‹€λ©΄ ν•˜λ‚˜μ˜ μ»΄ν¬λ„ŒνŠΈ μ•ˆμ— ν΄λž˜μŠ€κ°€ μ—¬λŸ¬κ°œ λ“€μ–΄κ°ˆ μˆ˜λ„ μžˆλ‹€.

λΆˆλ³€ν΄λž˜μŠ€μ˜ λ‹€λ₯Έ 예

public final class Complex { // 2. 상속 λΆˆκ°€λŠ₯
    private final double re;
    private final double im; // 3,4. λͺ¨λ“  ν•„λ“œλ₯Ό private final μ„ μ–Έ
		// 1. λ³€κ²½μž λ©”μ„œλ“œ μ—†μŒ
    public Complex(double re, double im) {
        this.re = re;
        this.im = im;
    }
    public double realPart() { return re; }
    public double imaginaryPart() { return im; }
    public Complex plus(Complex c) {
        return new Complex(re + c.re, im + c.im);
    }
    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);
    }
    @Override public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof Complex))
            return false;
        Complex c = (Complex) o;
        return Double.compare(c.re, re) == 0
               && Double.compare(c.im, im) == 0;
    }
    @Override public int hashCode() {
        return 31 * Double.hashCode(re) + Double.hashCode(im);
    }
    @Override public String toString() {
        return "(" + re + " + " + im + "i)";
    }
}
  • μœ„ ν΄λž˜μŠ€λŠ” λ³΅μ†Œμˆ˜(μ‹€μˆ˜λΆ€ ν—ˆμˆ˜λΆ€)λ₯Ό ν‘œν˜„ν•œλ‹€. Object λ©”μ„œλ“œλ₯Ό μž¬μ •μ˜ν–ˆκ³ , μ‹€μˆ˜λΆ€ ν—ˆμˆ˜λΆ€ 값을 λ°˜ν™˜ν•˜λŠ” μ ‘κ·Όμž λ©”μ„œλ“œμ™€ 사칙연산 λ©”μ„œλ“œλ₯Ό μ •μ˜ν–ˆλ‹€.

  • 사칙연산 λ©”μ„œλ“œλ“€μ΄ μΈμŠ€ν„΄μŠ€ μžμ‹ μ€ μˆ˜μ •ν•˜μ§€ μ•Šκ³  μƒˆλ‘œμš΄ Complex μΈμŠ€ν„΄μŠ€λ₯Ό λ§Œλ“€μ–΄ λ°˜ν™˜ν•˜λŠ” λͺ¨μŠ΅μ— μ£Όλͺ©

  • 이처럼 ν”Όμ—°μ‚°μžμ— ν•¨μˆ˜λ₯Ό μ μš©ν•΄ κ·Έ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•˜μ§€λ§Œ, ν”Όμ—°μ‚°μž μžμ²΄λŠ” κ·ΈλŒ€λ‘œμΈ ν”„λ‘œκ·Έλž˜λ° νŒ¨ν„΄μ„ ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ° 이라 ν•œλ‹€.
    (μœ„μ™€ 달리 절차적, λͺ…λ Ήν˜• ν”„λ‘œκ·Έλž˜λ°μ—μ„œλŠ” λ©”μ„œλ“œμ—μ„œ ν”Όμ—°μ‚°μžμΈ μžμ‹ μ„ μˆ˜μ •ν•΄ μžμ‹ μ˜ μƒνƒœκ°€ λ³€ν•œλ‹€.)

  • λ©”μ„œλ“œ μ΄λ¦„μœΌλ‘œ 동사(add) λŒ€μ‹  μ „μΉ˜μ‚¬(plus)λ₯Ό μ‚¬μš©ν•œ 점에도 μ£Όλͺ©ν•˜μž. μ΄λŠ” ν•΄λ‹Ή λ©”μ„œλ“œκ°€ 객체의 값을 λ³€κ²½ν•˜μ§€ μ•ŠλŠ”λ‹€λŠ” 사싀을 κ°•μ‘°ν•˜λ €λŠ” μ˜λ„. 참고둜, 이 λͺ…λͺ… κ·œμΉ™μ„ λ”°λ₯΄μ§€ μ•Šμ€ BigInteger와 BigDecimal 클래슀λ₯Ό μ‚¬λžŒλ“€μ΄ 잘λͺ» μ‚¬μš©ν•΄ 였λ₯˜κ°€ λ°œμƒν•˜λŠ” 일이 자주 μžˆλ‹€.

    BigDecimal test = new BigDecimal("1.111");
    
    // test1
    test.add(new BigDecimal("2.222"));
    System.out.println("test : " + test); // 1.111
    
    // test2
    test = test.add(new BigDecimal("2.222"));
    System.out.println("test : " + test); // 3.333
    
  • 동사 add 둜 λͺ…λͺ…λ˜μ–΄ μžˆμ–΄, 마치 ν”Όμ—°μ‚°μžμΈ μžμ‹ μ΄ 변경될 것 κ°™μ§€λ§Œ, 그렇지 μ•Šλ‹€. addλΌλŠ” μ΄λ¦„λ³΄λ‹€λŠ” plusκ°€ 더 μ–΄μšΈλ¦¬λŠ” λ©”μ„œλ“œμ΄λ‹€.
    ( κ·ΈλŸ¬λ‚˜ 이미 plusλΌλŠ” λ‹€λ₯Έ λ©”μ„œλ“œκ°€ μ‘΄μž¬ν•˜λ©°, λ‹€λ₯Έ κΈ°λŠ₯을 μˆ˜ν–‰ν•œλ‹€.)

μ™œ BigIntegerκ°€ λΆˆλ³€μΌκΉŒ?

  • BigDecimal의 addλ©”μ„œλ“œλ₯Ό μˆ˜ν–‰ν•˜λ©΄ 1.111을 담은 객체가 λ³€κ²½λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. ν”Όμ—°μ‚°μžλ₯Ό 가지고 μƒˆλ‘œ BigInteger 객체λ₯Ό μƒμ„±ν•˜κ²Œ λ©λ‹ˆλ‹€.

정리

  • ν΄λž˜μŠ€λŠ” κΌ­ ν•„μš”ν•œ κ²½μš°κ°€ μ•„λ‹ˆλΌλ©΄ λΆˆλ³€μ΄μ–΄μ•Ό ν•œλ‹€.
  • λΆˆλ³€ ν΄λž˜μŠ€λŠ” μž₯점이 많으며, 단점이라곀 νŠΉμ • μƒν™©μ—μ„œμ˜ 잠재적 μ„±λŠ₯ μ €ν•˜λΏμ΄λ‹€.
  • λ§Œμ•½ λΆˆλ³€μœΌλ‘œ λ§Œλ“€ 수 μ—†λŠ” ν΄λž˜μŠ€λΌλ„ λ³€κ²½ν•  수 μžˆλŠ” 뢀뢄을 μ΅œμ†Œν•œμœΌλ‘œ μ€„μ΄μž. λ‹€λ₯Έ ν•©λ‹Ήν•œ μ΄μœ κ°€ μ—†λ‹€λ©΄ λͺ¨λ“  ν•„λ“œλŠ” private final이어야 ν•œλ‹€.
  • μƒμ„±μžλŠ” λΆˆλ³€μ‹ 섀정이 λͺ¨λ‘ μ™„λ£Œλœ, μ΄ˆκΈ°ν™”κ°€ μ™„λ²½νžˆ λλ‚œ μƒνƒœμ˜ 객체λ₯Ό 생성해야 ν•œλ‹€.