다형성

  • 지금까지 본 바와 같이 상속은 결과적으로 코드 재사용이 올바른 목적은 아니다.
  • 상속은 타입 계층을 구조화하기 위해서 사용해야 한다.
  • 타입 계층은 객체지향의 특성 중 하나인 다형성의 기반을 제공한다.

다형성

  • polymorphism은 poly + morph의 합성어다.
  • universal 다형성과 adhoc 다형성으로 나눌 수 있다.
  • universal은 매개변수 다형성, 포함 다형성으로
  • adhoc은 오버로딩, 강제 다형성으로 분류할 수 있다.

    1. universal

    1.1. 매개변수(Parameter) 다형성
  • 제네릭 프로그래밍과 관련이 높은데 클래스의 인스턴스 변수나 메개변수 타입을 임시로 타입으로 선언한 후 사용하는 시점에 구체적인 타입으로 지정하는 방식
    1.2. 포함(Inclustion) 다형성
  • 메시지가 동일하더라도 수신한 객체의 타입에 따라 실제로 수행되는 행동이 달라지는 능력을 의미한다.
  • 서브타입 다형성이라고도 부른다.
  • 자식 클래스가 부모 클래스의 서브타입이어야 한다는 것이다.

    상속

    • 상속의 목적은 재사용이 아니라 프로그램 구성 개념들을 바탕으로 다형성을 가능하게 하는 타입 계층을 구축하기 위한 것이다. 상속의 양면성
      1. 상속의 이용은 부모 클래스에서 정의한 모든 데이터를 자식 클래스의 인스턴스에 자동으로 포함시킬 수 있다.: 데이터 관점의 상속
      • 자식ㄷ클래스 인스턴스 안에 부모 클래스의 인스턴스가 포함되는 것으로 생각하면 된다. 2. 메소드 역시 자동으로 자식 클래스에 포함시킬 수 있다.: 행동 관점의 상속
      • 부모 클래스가 정의한 일부 메소드를 자식 클래스의 메소드로 포함시키는 것

2. adhoc

2.1. 오버로딩(Overloading) 다형성
  • 하나 클래스 안에 동일한 이름의 메소드가 존재하는 경우
    2.2. 강제(Coercion) 다형성
  • 언어가 지원하는 자동적인 타입 변환이나 사용자가 구현한 타입 변환을 이용해서 동일한 연산자를 다양한 타입에 사용할 수 있는 방식

다형성 이용

  • 다형성을 이용할 수 있는 가장 큰 이유는 선언된 참조 타입과 무관하게 실제로 메시지를 수신하는 객체의 타입에 따라서 실행되는 메소드가 달라질 수 있다는 것이다.
  • 이는 업캐스팅, 동적 바인딩이 작용하기 때문이다.

동적 바인딩

  • 컴파일 타임에 호출할 함수를 결정하는 정적 바인딩, 초기 바인딩 또는 컴파일 타임 바인딩이 이있다.
  • 실행될 메소드를 런타임에 결정하는 동적 바인딩, 지연 바인딩이 있다.
  • 다형성은 동적 바인딩을 이용한다.

동적 메소드 탐색

  • 메시지 수신 객체는 먼저 자신을 생성한 클래스에 적합한 메소드가 존재하는지 검사하고 있으면 종료
  • 메소드를 찾지 못하면 부모 클래스에서 메소드 탐색을 지속한다. 찾을 때까지 작동한다.
  • 상속 최상층까지 갔는데 못찾으면 에러를 내린다.
  • 객체가 메시지를 수신하면 컴파일러는 self 참조라는 임시 변수를 자동으로 생성한 후 메시지를 수신한 객체를 가리킨다.
  • 동적 메소드 탐색은 self가 가리키는 객체의 클래스에서 시작해서 상속 계층의 역방향으로 이뤄지며 메소드 탐색이 종료되는 순간 self 참조는 자동으로 소멸된다.
  • 시스템은 class 포인터와 parent 포인터와 함께 self 참조를 조합해서 메소드를 탐색한다.
  • 결과적으로 동적 메소드 탐색은 두 가지 원리로 구성된다.
    1. 자동적 메시지 위임
    2. 동적인 문맥 사용
1. 자동적인 메시지 위임
  • 적절한 메소드를 찾을 때까지 상속 계층을 따라 부모 클래스로 처리가 위임된다.
    2. 동적인 문맥
  • 메시지를 수신한 객체가 무엇이냐에 따라서 메소드 탐색을 위한 문맥이 동적으로 바뀐다는 것이다.
  • 현재 객체에 메시지를 전송하는 것이다. 현재 객체라는 의미는 self가 가리키는 것이다.
  • self참조가 가리키는 자기 자신에게 메시지를 전송하는 것을 self 전송이라고 부른다.
3. 이해할 수 없는 메시지
  • 메시지가 상속에 끝에 도달했지만 이해할 수 없거나 처리할 수 없다면 결국 에러를 낸다.
4. self vs. super
  • self 참조는 동적이다. self는 메시지를 수신한 객체의 클래스에 따라 메소드 탐색을 위한 문맥을 실행 시점에 결정한다.
  • self는 메시지를 전송한다는 것은 맞지만 정확한 의도는 ‘이 클래스의 부모에서부터 메소드 탐색을 시작하라!’는 의미가 된다.

상속, 위임

  • 다형성은 self 참조가 가리키는 현재 객체에게 메시지를 전달하는 특성을 기반으로 한다.
  • 동일한 타입의 객체 참조에게 동일한 메시지를 전송하더라도 self 참조가 가리키는 객체의 클래스가 무엇이냐에 따라 탐색을 위한 문맥이 달라진다.