다형성
- 지금까지 본 바와 같이 상속은 결과적으로 코드 재사용이 올바른 목적은 아니다.
- 상속은 타입 계층을 구조화하기 위해서 사용해야 한다.
- 타입 계층은 객체지향의 특성 중 하나인 다형성의 기반을 제공한다.
다형성
- polymorphism은 poly + morph의 합성어다.
- universal 다형성과 adhoc 다형성으로 나눌 수 있다.
- universal은 매개변수 다형성, 포함 다형성으로
- adhoc은 오버로딩, 강제 다형성으로 분류할 수 있다.
1. universal
1.1. 매개변수(Parameter) 다형성
- 제네릭 프로그래밍과 관련이 높은데 클래스의 인스턴스 변수나 메개변수 타입을 임시로 타입으로 선언한 후 사용하는 시점에 구체적인 타입으로 지정하는 방식
1.2. 포함(Inclustion) 다형성
- 메시지가 동일하더라도 수신한 객체의 타입에 따라 실제로 수행되는 행동이 달라지는 능력을 의미한다.
- 서브타입 다형성이라고도 부른다.
- 자식 클래스가 부모 클래스의 서브타입이어야 한다는 것이다.
상속
- 상속의 목적은 재사용이 아니라 프로그램 구성 개념들을 바탕으로 다형성을 가능하게 하는 타입 계층을 구축하기 위한 것이다.
상속의 양면성
- 상속의 이용은 부모 클래스에서 정의한 모든 데이터를 자식 클래스의 인스턴스에 자동으로 포함시킬 수 있다.: 데이터 관점의 상속
- 자식ㄷ클래스 인스턴스 안에 부모 클래스의 인스턴스가 포함되는 것으로 생각하면 된다. 2. 메소드 역시 자동으로 자식 클래스에 포함시킬 수 있다.: 행동 관점의 상속
- 부모 클래스가 정의한 일부 메소드를 자식 클래스의 메소드로 포함시키는 것
- 상속의 목적은 재사용이 아니라 프로그램 구성 개념들을 바탕으로 다형성을 가능하게 하는 타입 계층을 구축하기 위한 것이다.
상속의 양면성
2. adhoc
2.1. 오버로딩(Overloading) 다형성
- 하나 클래스 안에 동일한 이름의 메소드가 존재하는 경우
2.2. 강제(Coercion) 다형성
- 언어가 지원하는 자동적인 타입 변환이나 사용자가 구현한 타입 변환을 이용해서 동일한 연산자를 다양한 타입에 사용할 수 있는 방식
다형성 이용
- 다형성을 이용할 수 있는 가장 큰 이유는 선언된 참조 타입과 무관하게 실제로 메시지를 수신하는 객체의 타입에 따라서 실행되는 메소드가 달라질 수 있다는 것이다.
- 이는 업캐스팅, 동적 바인딩이 작용하기 때문이다.
동적 바인딩
- 컴파일 타임에 호출할 함수를 결정하는 정적 바인딩, 초기 바인딩 또는 컴파일 타임 바인딩이 이있다.
- 실행될 메소드를 런타임에 결정하는 동적 바인딩, 지연 바인딩이 있다.
- 다형성은 동적 바인딩을 이용한다.
동적 메소드 탐색
- 메시지 수신 객체는 먼저 자신을 생성한 클래스에 적합한 메소드가 존재하는지 검사하고 있으면 종료
- 메소드를 찾지 못하면 부모 클래스에서 메소드 탐색을 지속한다. 찾을 때까지 작동한다.
- 상속 최상층까지 갔는데 못찾으면 에러를 내린다.
- 객체가 메시지를 수신하면 컴파일러는 self 참조라는 임시 변수를 자동으로 생성한 후 메시지를 수신한 객체를 가리킨다.
- 동적 메소드 탐색은 self가 가리키는 객체의 클래스에서 시작해서 상속 계층의 역방향으로 이뤄지며 메소드 탐색이 종료되는 순간 self 참조는 자동으로 소멸된다.
- 시스템은 class 포인터와 parent 포인터와 함께 self 참조를 조합해서 메소드를 탐색한다.
- 결과적으로 동적 메소드 탐색은 두 가지 원리로 구성된다.
- 자동적 메시지 위임
- 동적인 문맥 사용
1. 자동적인 메시지 위임
- 적절한 메소드를 찾을 때까지 상속 계층을 따라 부모 클래스로 처리가 위임된다.
2. 동적인 문맥
- 메시지를 수신한 객체가 무엇이냐에 따라서 메소드 탐색을 위한 문맥이 동적으로 바뀐다는 것이다.
- 현재 객체에 메시지를 전송하는 것이다. 현재 객체라는 의미는 self가 가리키는 것이다.
- self참조가 가리키는 자기 자신에게 메시지를 전송하는 것을 self 전송이라고 부른다.
3. 이해할 수 없는 메시지
- 메시지가 상속에 끝에 도달했지만 이해할 수 없거나 처리할 수 없다면 결국 에러를 낸다.
4. self vs. super
- self 참조는 동적이다. self는 메시지를 수신한 객체의 클래스에 따라 메소드 탐색을 위한 문맥을 실행 시점에 결정한다.
- self는 메시지를 전송한다는 것은 맞지만 정확한 의도는 ‘이 클래스의 부모에서부터 메소드 탐색을 시작하라!’는 의미가 된다.
상속, 위임
- 다형성은 self 참조가 가리키는 현재 객체에게 메시지를 전달하는 특성을 기반으로 한다.
- 동일한 타입의 객체 참조에게 동일한 메시지를 전송하더라도 self 참조가 가리키는 객체의 클래스가 무엇이냐에 따라 탐색을 위한 문맥이 달라진다.