item 18 leekyunghee - JAVA-JIKIMI/EFFECTIVE-JAVA3 GitHub Wiki

μƒμ†λ³΄λ‹€λŠ” μ»΄ν¬μ§€μ…˜μ„ μ‚¬μš©ν•˜λΌ.

  • μ½”λ“œ μž¬μ‚¬μš©μ˜ κ°•λ ₯ν•œ μˆ˜λ‹¨
  • μƒμœ„ ν΄λž˜μŠ€μ™€ ν•˜μœ„ 클래슀λ₯Ό λͺ¨λ‘ 같은 ν”„λ‘œκ·Έλž˜λ¨Έκ°€ ν†΅μ œν•˜λŠ” νŒ¨ν‚€μ§€ μ•ˆμ—μ„œλΌλ©΄ 상속도 μ•ˆμ „ν•œ 방법
  • ν™•μž₯ν•  λͺ©μ μœΌλ‘œ μ„€κ³„λ˜μ—ˆκ³  λ¬Έμ„œν™”λ„ 잘 된 ν΄λž˜μŠ€λ„ μ•ˆμ „ν•˜λ‹€.

λ©”μ„œλ“œ 호좜과 달리 상속은 μΊ‘μŠν™”λ₯Ό κΉ¨λœ¨λ¦°λ‹€.

  • μƒμœ„ ν΄λž˜μŠ€μ— 따라 ν•˜μœ„ 클래슀의 λ™μž‘μ— 이상이 생길 수 μžˆλ‹€.
// μƒμ†μ˜ 잘λͺ»λœ μ‚¬μš© 
public class InstrumentedHashSet<E> extends HashSet<E> {
    // μΆ”κ°€λœ μ›μ†Œμ˜ 수
    private int addCount = 0;

    public InstrumentedHashSet() {
    }

    public InstrumentedHashSet(int initCap, float loadFactor) {
        super(initCap, loadFactor);
    }

    @Override public boolean add(E e) {
        addCount++;
        return super.add(e);
    }

    @Override public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }

    public int getAddCount() {
        return addCount;
    }

    // addAll λ©”μ„œλ“œλ‘œ μ›μ†Œ 3개λ₯Ό λ”ν–ˆμ„ 경우 
    public static void main(String[] args) {
        InstrumentedHashSet<String> s = new InstrumentedHashSet<>();
        s.addAll(List.of("ν‹±", "탁탁", "νŽ‘"));    // Arrays.asList 
        System.out.println(s.getAddCount());    // 3을 λ°˜ν™˜ν•  κ²ƒμœΌλ‘œ μ˜ˆμƒν•˜λ‚˜ 6을 λ°˜ν™˜ 
    }
}
// HashSet의 addAll λ©”μ„œλ“œκ°€ add λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄ κ΅¬ν˜„λœ 데 μžˆλ‹€. 
// InstrumentedHashSet의 addAll은 addCount에 3을 λ”ν•œ ν›„ HashSet의 addAll κ΅¬ν˜„μ„ 호좜
// ν•˜μœ„ ν΄λž˜μŠ€μ—μ„œ addAll λ©”μ„œλ“œλ₯Ό μž¬μ •μ˜ ν•˜μ§€ μ•ŠμœΌλ©΄ 문제λ₯Ό κ³ μΉ  수 μžˆλ‹€. 

κΈ°λ³Έ 클래슀λ₯Ό ν™•μž₯ν•˜λŠ” λŒ€μ‹  μƒˆλ‘œμš΄ 클래슀λ₯Ό λ§Œλ“€κ³  private ν•„λ“œλ‘œ κΈ°μ‘΄ 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό μ°Έμ‘°ν•˜κ²Œ ν•˜μž.

κΈ°μ‘΄ ν΄λž˜μŠ€κ°€ μƒˆλ‘œμš΄ 클래슀의 κ΅¬μ„±μš”μ†Œλ‘œ μ“°μΈλ‹€λŠ” 섀계 - composition

μƒˆ 클래슀의 μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œλ“€μ€ (private ν•„λ“œλ‘œ μ°Έμ‘°ν•˜λŠ”) κΈ°μ‘΄ 클래슀의 λŒ€μ‘ν•˜λŠ” λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•΄ κ·Έ κ²°κ³Όλ₯Ό λ°˜ν™˜ν•œλ‹€. (aka 전달)

μƒˆ 클래슀의 λ©”μ„œλ“œλ“€μ„ 전달 λ©”μ„œλ“œλΌκ³  ν•œλ‹€.

//Wrapper class 상속 λŒ€μ‹  μ»΄ν¬μ§€μ…˜μ„ μ‚¬μš©
public class InstrumentedSet<E> extends ForwardingSet<E> {
    private int addCount = 0;

    public InstrumentedSet(Set<E> s) {
        super(s);
    }

    @Override public boolean add(E e) {
        addCount++;
        return super.add(e);
    }
    @Override public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }
    public int getAddCount() {
        return addCount;
    }

    public static void main(String[] args) {
        InstrumentedSet<String> s = new InstrumentedSet<>(new HashSet<>());
        s.addAll(List.of("ν‹±", "탁탁", "νŽ‘"));
        System.out.println(s.getAddCount());
    }
}
// μž¬μ‚¬μš©ν•  수 μžˆλŠ” 전달 클래슀 
public class ForwardingSet<E> implements Set<E> {
    private final Set<E> s;
    public ForwardingSet(Set<E> s) { this.s = s; }

    public void clear()               { s.clear();            }
    public boolean contains(Object o) { return s.contains(o); }
    public boolean isEmpty()          { return s.isEmpty();   }
    public int size()                 { return s.size();      }
    public Iterator<E> iterator()     { return s.iterator();  }
    public boolean add(E e)           { return s.add(e);      }
    public boolean remove(Object o)   { return s.remove(o);   }
    public boolean containsAll(Collection<?> c)
                                   { return s.containsAll(c); }
    public boolean addAll(Collection<? extends E> c)
                                   { return s.addAll(c);      }
    public boolean removeAll(Collection<?> c)
                                   { return s.removeAll(c);   }
    public boolean retainAll(Collection<?> c)
                                   { return s.retainAll(c);   }
    public Object[] toArray()          { return s.toArray();  }
    public <T> T[] toArray(T[] a)      { return s.toArray(a); }
    @Override public boolean equals(Object o)
                                       { return s.equals(o);  }
    @Override public int hashCode()    { return s.hashCode(); }
    @Override public String toString() { return s.toString(); }
}

// λ‹€λ₯Έ Set에 계측 κΈ°λŠ₯을 λ§μ”Œμš΄λ‹€λŠ” λœ»μ—μ„œ Decorator νŒ¨ν„΄μž„ 
// 래퍼 객체가 λ‚΄λΆ€ 객체에 자기 μžμ‹ μ˜ μ°Έμ‘°λ₯Ό λ„˜κΈ°λŠ” 경우만 μœ„μž„μ— ν•΄λ‹Ήν•œλ‹€. 
  • νŒ¨ν‚€μ§€ λ°”κΉ₯μ—μ„œ μ ‘κ·Όν•  수 μžˆλŠ” 클래슀라면 μ ‘κ·Όμžλ₯Ό μ œκ³΅ν•˜λ―€λ‘œμ¨ 클래슀 λ‚΄λΆ€μ˜ ν‘œν˜„ 방식을 μ–Έμ œλ“ μ§€ λ°”κΏ€ 수 있음
  • public ν΄λž˜μŠ€κ°€ ν•„λ“œλ₯Ό κ³΅κ°œν•˜λ©΄ 이λ₯Ό μ‚¬μš©ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈκ°€ 생겨날 κ²ƒμ΄λ―€λ‘œ λ‚΄λΆ€ ν‘œν˜„ 방식을 λ§ˆμŒλŒ€λ‘œ λ°”κΏ€ 수 μ—†κ²Œ λœλ‹€.

package-private 클래슀 ν˜Ήμ€ private 쀑첩 클래슀라면 데이터 ν•„λ“œλ₯Ό λ…ΈμΆœν•œλ‹€ 해도 ν•˜λ‹Ήμ˜ λ¬Έμ œκ°€ μ—†λ‹€.

public 클래슀의 ν•„λ“œλ₯Ό 직접 λ…ΈμΆœν•˜μ§€ λ§λΌλŠ” κ·œμΉ™μ„ μ–΄κΈ΄ 사둀

  • java.awt.package

Point, Dimension class (item 67)

μΈμŠ€ν„΄μŠ€ ν•„λ“œ λΆˆλ³€μΌ 경우 단점

  • public 클래슀의 ν•„λ“œκ°€ λΆˆλ³€μ΄λΌλ©΄ 직접 λ…ΈμΆœν•  λ•Œμ˜ 단점이 μ‘°κΈˆμ€ 쀄어 λ“€μ§€λ§Œ APIλ₯Ό λ³€κ²½ν•˜μ§€ μ•Šκ³ λŠ” ν‘œν˜„ 방식을 λ°”κΏ€ 수 μ—†μŒ
  • ν•„λ“œλ₯Ό 읽을 λ•Œ λΆ€μˆ˜ μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μ—†λ‹€.

핡심 정리

클래슀 Bκ°€ 클래슀 A와 is-a 관계일 λ•Œλ§Œ 클래슀 Aλ₯Ό 상속해야 ν•œλ‹€. 

- AλŠ” B 쀑 ν•˜λ‚˜
- AλŠ” Bλ₯Ό 상속함
- AλŠ” B의 μ„œλΈŒν΄λž˜μŠ€
- AλŠ” B의 속성을 λͺ¨λ‘ κ³„μŠΉν•¨

상속은 κ°•λ ₯ν•˜μ§€λ§Œ μΊ‘μŠν™”λ₯Ό ν•΄μΉœλ‹€λŠ” 문제 

μƒμœ„ ν΄λž˜μŠ€κ°€ ν™•μž₯을 κ³ λ €ν•΄ μ„€κ³„λ˜μ§€ μ•Šμ•˜λ‹€λ©΄ is-a 관계일 λ•Œλ„ λ¬Έμ œκ°€ 될 수 있음

* μƒμ†μ˜ 취약점을 ν”Όν•˜λ €λ©΄ 상속 λŒ€μ‹  μ»΄ν¬μ§€μ…˜κ³Ό 전달을 μ‚¬μš©ν•˜μž. 

⚠️ **GitHub.com Fallback** ⚠️