> For the complete documentation index, see [llms.txt](https://incheol-jung.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://incheol-jung.gitbook.io/docs/study/object/2020-03-10-object-chap12.md).

# 12장 다형성

## Intro

* 상속의 목적은 코드 재사용이 아니다.
* 상속은 타입 계층을 `구조화`하기 위해 사용해야 한다.
* 타입 계층은 객체 지향 프로그래밍의 중요한 특성 중 하나인 `다형성`의 기반을 제공한다.
* 상속을 이용해 자식 클래스를 추가하려 한다면 스스로에게 다음과 같은 질문을 해보자
  * 단순히 코드를 `재사용`하기 위해서인가? → **예, 라고 생각한다면 상속을 사용하지 말아라**
  * 인스턴스들을 동일하게 행동하는 `그룹`으로 묶기 위해서인가? → **예, 라고 생각한다면 사용해도 된다.**
* 많은 시간이 흐른 지금도 여전히 상속은 `다형성`을 구현할 수 있는 가장 일반적인 방법이다.&#x20;

  하지만 최근의 언어들은 상속 이외에도 다형성을 구현할 수 있는 다양한 방법들을 제공하고 있기 때문에 과거에 비해 상속의 중요성이 많이 낮아졌다고 할 수 있다.
* 이번 장을 통해서 다형성이 `런타임`에 메시지를 처리하기에 적합한 메서드를 동적으로 탐색하는 과정을 통해 구현 되며 상속이 이런 메서드를 찾기 위한 일종의 탐색 경로를 클래스 계층의 형태로 구현하기 위한 방법이라는 사실을 이해할 것이다.

## 다형성

* `다형성`이라는 단어는 ‘많은’을 의미하는 ‘`poly`’와 ‘형태’를 의미하는 ‘`morph`’의 합성어로 ‘많은 형태를 가질 수 있는 능력’을 의미한다.
* 다형성의 분류
  * 유니버셜 : `타입`이 달라도 같은 `code`를 수행
    * 매개변수 : 제네릭 프로그래밍과 관련이 높은데 클래스의 인스턴스 변수나 메서드의 매개변수 타입을 `임의의 타입`으로 선언한 후 사용하는 시점에 구체적인 타입으로 지정하는 방식 ex) List
    * 포함 : 메시지가 동일 하더라도 수신한 객체의 타입에 따라 실제로 수행되는 행동이 달라 지는 기능 Ex) discountPolicy.calculate()
  * 임시 : 타입이 다르면 다른 version의 code를 수행
    * 오버로딩 : 하나의 클래스 안에 `동일한 이름`의 메서드가 존재하는 경우 ex) plus(int a), plus(double a)
    * 강제 : 언어가 지원하는 자동적인 타입 변환이나 사용자가 직접 구현한 타입 변환을 이용해 동일한 연산자를 다양한 타입에 사용할 수 있는 방식 ex) ‘+’ 연산자 → 정수형 + 정수형, 문자열 + 정수형
* 상속의 진정한 목적은 코드 재사용이 아니라 `다형성`을 위한 **`서브타입 계층`**&#xC744; 구축하는 것이다.

## 상속의 양면성

* 객체지향 프로그램을 작성하기 위해서는 항상 `데이터`와 `행동`이라는 두 가지 관점을 함께 고려해야 한다.
* 데이터 관점의 상속 : 상속을 이용하면 부모 클래스에서 정의한 `모든 데이터`를 자식 클래스의 인스턴스에 자동으로 `포함`시킬 수 있다.
* 행동 관점의 상속 : 데이터뿐만 아니라 부모 클래스에서 정의한 `일부 메서드` 역시 자동으로 자식 클래스에 `포함`시킬 수 있다.
* 단순히 데이터와 행동의 관점에서만 바라보면 상속이란 부모 클래스에서 정의한 데이터와 행동을 자식 클래스에서 자동적으로 `공유`할 수 있는 재사용 매커니즘으로 보일 것이다. 하지만 이 관점은 상속을 오해한 것이다.

## 업캐스팅과 동적 바인딩

### 같은 메시지, 다른 메서드

* **업캐스팅** : 부모 클래스 타입으로 선언된 변수에 자식 클래스의 인스턴스를 `할당`하는 것이 가능하다.
* **동적 바인딩** : 메시지를 처리할 적절한 메서드를 컴파일 시점이 아니라 `실행 시점에 결정`한다.
* `업캐스팅`과 `동적 메서드 탐색`은 코드를 변경하지 않고도 기능을 추가할 수 있게 해주며 이것은 `개방-폐쇄 원칙`의 의도 와도 일치한다.

### 동적바인딩

* `함수 호출`과 `메시지 전송` 사이의 차이는 생각보다 큰데 프로그램 안에 작성된 함수 호출 구문과 실제로 실행되는 코드를 연결하는 언어적인 매커니즘이 완전히 다르기 때문이다.
* 함수 호출 : `컴파일 타임`에 결정 (정적 바인딩, 초기 바인딩, 컴파일타임 바인딩)
* 메시지 전송 : 실행될 메서드를 `런타임`에 결정 (동적 바인딩, 지연바인딩)

## 동적 메서드 탐색과 다형성

* 동적 메서드 탐색은 `self`가 가리키는 객체의 클래스에서 시작해서 상속 계층의 `역방향`으로 이뤄지며 메서드 탐색이 종료되는 순간 self 참조는 자동으로 소멸된다.
* 동적 메서드 탐색 원리
  * 자동적인 메시지 위임 : 자식 클래스는 자신이 이해할 수 없는 메시지를 전송받은 경우 상속 계층에 따라 부모 클래스에게 `처리를 위임`한다.
  * 동적인 문맥 : 메시지를 수신 했을 때 실제로 어떤 메서드를 실행 할지를 결정하는 것은 컴파일 시점이 아닌 실행 시점에 이뤄지며, 메서드를 탐색하는 경로는 `self 참조`를 이용해서 결정한다.

### 동적인 문맥

```java
public class Lecture {
    public String stats() {
        return String.format("Title: %s, Evaluation Method: %s", title, getEvaluationMethod());
    }

    public String getEvaluationMethod() {
        return "Pass or Fail";
    }
}
```

* `getEvaluationMethod` 메서드를 호출한다고 표현했지만 사실 이 말은 정확하지는 않다.
* 현재 클래스의 메서드를 호출하는 것이 아니라 현재 객체에게 메시지를 전송하는 것이다.
* 그렇다면 현재 객체한 무엇인가? 바로 self 참조가 가리키는 객체다. 이 객체는 처음에 `stats` 메시지를 수신했던 바로 그 객체다.

  ```java
  public class GradeLecture extends Lecture {
    @Override
    public String getEvaluationMethod() {
        return "Grade";
    }
  }
  ```
* `GradeLecture` 클래스에 `stats` 메시지를 전송하면 `Lecture` 클래스의 `stats` 메서드를 실행하는 중에 `self` 참조가 가리키는 객체에게 `getEvaluationMethod` 메시지를 전송하는 구문과 마주치게 된다. 이제 메서드 탐색은 `self 참조`가 가리키는 객체에서 시작된다. 여기서 `self 참조`가 가리키는 객체는 바로 `GradeLecture`의 인스턴스다. 따라서 메시지 탐색은 `Lecture` 클래스를 벗어나 self 참조가 가리키는 `GradeLecture`에서 부터 다시 시작된다.
* 417, 419 페이지 참조

### 이해할 수 없는 메시지

* `정적 타입 언어`에서는 코드를 `컴파일`할 때 상속 계층 안의 클래스들이 메시지를 이해할 수 있는지 여부를 판단한다.
* `동적 타입 언어` 역시 메시지를 수신한 객체의 클래스부터 부모 클래스의 방향으로 메서드를 탐색한다. 차이점이라면 동적 타입 언어에는 컴파일 단계가 존재하지 않기 때문에 실제로 코드를 실행 해보기 전에는 메시지 처리 가능 여부를 판단할 수 없다는 점이다.
  * 만약 상속 계층 안의 어떤 클래스도 메시지를 처리할 수 없다면 `메서드 탐색`은 다시 한번 최상위 클래스에 이르게 되고 최종적으로 `예외 메시지`를 던지게 된다.
  * `doesNotUnderstand`(스몰토크)나 `method_missing`(루비) 메시지에 응답할 수 있는 메서드를 구현할 수 있다.
* 이해할 수 없는 메시지를 처리할 수 있는 동적 타입 언어의 특징은 메타 프로그래밍 영역에서 진가를 발휘한다. 마틴 파울러는 동적 타입 언어의 이러한 특징을 이용해 `도메인-특화 언어`를 개발하는 방식을 동적 리셉션이라고 부른다.

### self 대 super

* self 참조의 가장 큰 특징은 `동적`이라는 점이다. self 참조는 메시지를 수신한 객체의 클래스에 따라 메서드 참색을 위한 문맥을 실행 시점에 결정한다.
* super 참조는 부모 클래스에서부터 메서드 탐색을 시작한다는 의미이다.

## 상속 대 위임

### 위임과 self 참조

* 메서드 탐색 중에는 `자식 클래스`의 인스턴스와 `부모 클래스`의 인스턴스가 동일한 `self 참조`를 공유하는 것으로 봐도 무방하다.
* 따라서 그림 12.20을 그림 12.21과 같이 바꿀 수 있다. 그리고 상속 계층을 구성하는 객체들 사이에서 self 참조를 공유하기 때문에 개념적으로 각 인스 턴스에서 self 참조를 공유하는 self라는 변수를 포함하는 것처럼 표현할 수 있다.
* 426p 참고
