# Item 18. 상속 대신 컴포지션을 사용하라

## 상속의 위험성

### 상속

```java
class MySet<E> extends HashSet<E> {

    @Override
    public int size() {
        return super.size();
    }

}
```

### 상속은 캡슐화를 깨트림

* 상위 클래스의 내부 구현이 달라졌을 때, 이 클래스의 API만을 이용하는 외부 클라이언트는 영향을 받지 않음
* 그러나 이를 상속하는 하위 클래스는, 내부 구현 변경에 의해 영향을 받을 수 있음
  * 왜? 하위 클래스에서는 상위 클래스를 오버라이딩할 수 있고, 상위 클래스에서 내부 구현 상 내부의 메서드를 호출하는 과정에서 다른 내부 메서드를 호출하게 되면 오버라이딩된 메서드를 호출하게 됨
  * 따라서 자기사용(self-use) 여부에 따라 동작이 달라질 수 있으며, 이는 내부 구현 방식에 해당
* 또한, 상위 클래스에 새로운 메서드가 추가되거나 하는 이유로 동작이 달라질 수 있음

### 해결책과 뒤따르는 문제점들

* 상위 클래스의 메서드를 오버라이딩하는 과정에서 super를 호출하는 대신 아예 동작 자체를 재정의할 수 있으나, 이는 복잡하고 성능 저하를 일으킬 수 있음
* 아예 이름만 같고 반환 타입/매개 인자가 다른 메서드를 새로 선언할 수 있으나, 이 역시 아래의 문제점들을 야기
  * 상위 클래스의 새 릴리스에 시그니처가 같고 반환 타입이 다른 메서드가 추가됨 - 생성한 메서드가 무시됨
  * 상위 클래스의 새 릴리스에 시그니처, 반환 타입이 모두 같은 메서드가 추가됨 - 단순 오버라이딩처리되어 오동작 위험이 있음

### 문제 요인

* 하위 클래스의 재정의가 상위 클래스의 동작에도 영향을 미치게 되기 때문에 내부 구현에 영향을 받게 됨
* 상위 클래스와 하위 클래스는 강하게 연결되며, 상위 클래스의 새 변경점에 하위 클래스는 큰 영향을 받음
* 이는 하위 클래스가 사실은 상위 클래스와 `is-a 관계`가 아닌데 상속하기 때문에 주로 발생하는 문제들로, 이러한 경우엔는 상속 대신 `컴포지션(Composition, 합성)`을 이용해야 함

## 컴포지션

```java
class MySet<E> implements Set<E> {

    private final Set<E> set;

    // Set 인터페이스의 메서드들을 구현
    // 포워딩 방식을 이용
    @Override
    public int size() {
        return set.size();
    }

}
```

* 기존 클래스를 확장하는 대신, 이 클래스의 인스턴스를 내부 필드로 참조하게 하는 설계
* 새 클래스는 기존 클래스의 메서드를 호출하게 되고, 이를 처리를 넘긴다는 의미로 `위임(delegation)`이라고 부름
* 또한, 새 클래스는 다른 인스턴스를 감싸는 역할을 한다는 뜻에서 `래퍼(Wrapper) 클래스`라고 부름
* 위와 같이 컴포지션을 이용하면, Set 인터페이스를 구현한 구체 클래스를 상속하는 대신 어떤 Set의 기능이라도 유연하게 이용이 가능
* MySet은 Set에 특정한 기능을 덧씌울 수 있는 클래스라는 점에서 `데코레이터(Decorator) 패턴`을 이용했다고 볼 수 있음

## 언제 상속을 사용?

* 하위 클래스가 상위 클래스와 정확히 `is-a 관계`일 때에만 상속을 이용할 수 있음
  * 리스코프 치환 원칙(서브 타입은 언제나 기반 타입으로 교체할 수 있어야 한다) 에서 또한 이 점을 강조하고 있음
  * 이를 만족하는 방법은, 하위 클래스를 상위 클래스로 대체했을 때 모든 동작이 정상적인가? 를 보면 됨
* 그러나 상속을 이용하면 하위 클래스는 상위 클래스의 결함을 그대로 물려받게 되며, 상위 클래스가 확장을 고려해 설계되지 않았다면 오버라이딩한 메서드에 의해 다양한 문제가 발생할 수 있음
* 따라서 상속 대신 컴포지션을 사용하는 편이 더 유연한 클래스를 정의할 수 있음
