12. 다형성

다형성
여러 타입을 대상으로 동작하는 코드를 작성할 수 있는 방법

다형성은 런타임에 메시지를 처리하기에 적합란 메서드를 동적으로 탐색하는 과정을 통해 구현되며, 상속은 이런 메서드를 찾기 위한 일종의 탐색 경로를 클래스의 계층의 형태로 구현하기 위한 방법이다.

상속의 관점에서 다형성이 구현되는 기술적인 메커니즘을 알아보자.

다형성

객체지향 프로그래밍에서 사용되는 다형성은 아래 그림과 같이 유니버설(Universal) 다형성과 임시(Ad Hoc)다형성으로 분류할 수 있다. 유니버설 다형성은 다시 매개변수 다형성과 포함 다형성으로 분류할 수 있고, 임시 다형성은 오버로딩 다형성과 강제다형성으로 분류할 수 있다.

12.1

매개변수 다형성(혹은 제네릭 다형성)
- 클래스의 인스턴스 변수나 메서드의 매개변수 타입을 임의의 타입으로 선언한 후 사용하는 시점에 구체적인 타입으로 지정하는 방식.

포함 다형성
- 메시지가 동일하더라도 수신한 객체의 타입에 따라 실제로 수행되는 행동이 달라지는 능력. 서브타입 다형성이라고 부르기도 한다.

오버로딩 다형성
- 하나의 클래스 안에 동일한 이름의 메서드가 존재하는 경우를 가리켜 오버로딩 다형성이라 함.

강제 다형성
- 언어가 지원하는 자동적 타입 변환이나 사용자가 직접 변환한 타입 변환을 이용해 동일한 연산을 다양한 타입에 적용해 사용할 수 있는 방식.

상속의 메커니즘

상속에는 업캐스팅, 동적 바인딩, 동적 메서드 탐색, self 참조, super 참조의 개념이 있다. 이 개념들을 이해하고 나면 상속 내부의 메커니즘 뿐만 아니라 타입계층을 기반으로 한 다형성의 동작 방식을 이해할 수 있을 것이다.

상속의 목적
상속의 목적은 코드 재사용이 아니다. 상속은 프로그램을 구성하는 개념을 기반으로 다형성을 가능하게 하는 타입 계층을 구축하기 위한 것이다.

업캐스팅

상속을 이용하면 부모 클래스의 퍼블릭 인터페이스가 자식 클래스의 퍼블릭 인터페이스에 합쳐지기 때문에 부모 클래스에 전송할 수 있는 메시지를 자식 클래스의 인스턴스에 전송이 가능하다. 부모 클래스의 인스턴스 대신 자식 클래스의 인스턴스에 메시지를 전송하더라도 메시지를 처리하는 데는 아무런 문제가 없으며, 컴파일러는 명시적 타입 변환이 없어도 자식 클래스가 부모 클래스를 대체할 수 있도록 허용한다. 부모 클래스의 타입으로 선언된 변수에 자식 클래스의 인스턴스를 할당하는것이 가능하다. 이를 업캐스팅이라 한다.

12.7

동적 바인딩

실행될 메서드를 런타임에 결정하는 방식으로, 지연 바인딩 이라고 부른다. foo.bar()라는 코드를 읽는것 만으로는 실행되는 bar가 어떤 클래스에 어떤 메서드인지 판단하기가 어렵다. foo가 가리키는 객체가 실제로 어떤 클래스의 인스턴스인지 알아야 하고 bar 메서드가 해당 클래스의 상속계층의 어디에 위치하는지를 알아야 한다.

동적 메서드 탐색

시스템은 class 포인터(자신의 클래스를 가리키는 포인터)와 parent 포인터(부모클래스의 위치를 가리키는 포인터)포인터와 함께 self 참조를 조합해 메서드를 탐색한다. 다음과 같은 과정을 따른다.

  • 메시지를 수신한 객체는 먼저 자신을 생성한 클래스(동적인 문맥)에 적합한 메서드가 존재하는지 검사한다. 존재하면 메서드를 실행하고 탐색을 종료한다.
    • 자신의 타입이 아닌 자신을 생성한 클래스가 메서드 탐색의 시작점이다.
    • 메서드가 오버라이딩 되는 이유이기도 하다.
  • 메서드를 찾지 못했다면 부모 클래스에서 메서드 탐색(자동적인 메시지 위임)을 계속한다. 이 과정은 적합한 메서드를 찾을때까지 상속 계층을 따라 올라가며 계속된다.
  • 상속 계층의 가장 최상위 클래스에 이르렀지만 메서드를 발견하지 못한 경우 예외를 발생시키며 탐색을 종료한다.
    • 정적타입 언어에서는 컴파일 타임에 체크를 하여 메시지를 이해할 수 없는 경우 컴파일 에러를 발생시킨다.
    • 동적타입 언어에서는 self참조가 가리키는 현재 객체에게 이해할 수 없다는 메시지를 전송한다.

객체가 메시지를 수신하면 컴파일러는 self 참조라는 임시 변수를 생성한 후에 메시지를 수신한 객체를 가리키도록 설정한다. 동적 탐색은 self 객체가 가리키는 객체의 클래스에서 시작하여 상속 계층의 역방향으로 이루어지며 메서드 탐색이 종료되는 순간 self 참조는 자동으로 소멸된다.

self 와 super

  • self 전송 : 메시지를 수신하는 객체의 클래스에 따라 메서드 탐색을 시작할 위치를 동적으로 결정.
  • super 전송 : 항상 메시지를 전송하는 클래스의 부모 클래스로부터 시작

클래스기반 언어, 프로토타입 기반 언어에서의 상속과 위임

위임
자신이 수신한 메시지를 다른 객체에게 전달해 처리를 요청하는 것을 위임이라 부른다. 위임은 본질적으로 자신이 정의하지 않거나 처리할 수 없눈 속성 또는 메서드의 탐색 과정을 다른 객체로 이동시키기 위해 사용한다. 이를 위해 위임은 항상 현재의 실행 문맥을 가리키는 self 참조를 인자로 전달한다. 위임은 객체 사이의 동적인 연결관계를 이용해 상속을 구현하는 방법이다. (p.429)

클래스 기반 언어

자식 클래스에서 부모 클래스로 self 참조를 전달하는 매커니즘(위임)으로 상속을 바라볼 수 있다.

프로토타입 언어: 위임을 통해 상속 구현

클래스를 지원하지 않는 프로토타입 기반 언어에서도 위임의 개뇸을 이용하여 상속을 구현할 수 있다. 클래스 기반 언어들이 상속을 이용해 클래스 사이에 self 참조를 자동으로 전달해 주는 것처럼 프로토타입 기반 객체지향 언어 역시 위임을 사용해 객체 사이에 self 참조를 자동으로 전달한다.

Last Updated: 1/28/2020, 10:16:33 AM