[Object] 상속

업데이트:

상속을 이용하면 부모 클래스에서 정의한 모든 데이터를 자식 클래스의 인스턴스에 자동으로 포함시킬 수 있다. 이것이 데이터 관점의 상속이다. 데이터 뿐만 아니라 부모 클래스에서 정의한 일부 메서드 역시 자동으로 자식 클래스에 포함시킬 수 있다. 이것이 행동 관점의 상속이다.

상속의 목적은 코드 재사용이 아니다. 상속은 프로그램을 구성하는 개념들을 기반으로 다형성을 가능하게 하는 타입 계층을 구축하기 위한 것이다. 타입 계층에 대한 고민 없이 코드를 재사용하기 위해 상속을 사용하면 이해하기 어렵고 유지보수하기 버거운 코드가 만들어질 확률이 높다.

자식 클래스 안에 상속받은 메서드와 동일한 시그니처의 메서드를 재정해서 부모 클래스의 구현을 새로운 구현으로 대체하는 것을 메서드 오버라이딩 이라고 부른다.

업케스팅

부모 클래스 타입으로 선언된 변수에 자식 클래스의 인스턴스를 할당하는 것이 가능하다. 이를 업캐스팅이라고 부른다.

컴파일러 관점에서 자식 클래스는 아무런 제약 없이 부모 클래스를 대체할 수 있기 때문에 부모 클래스와 협력하는 클라이언트는 다양한 자식 클래스의 인스턴스와도 협력하는 것이 가능하다. 여기서 자식 클래스는 현재 상속 계층에 존재하는 자식 클래스뿐만 아니라 앞으로 추가될지도 모르는 미래의 자식 클래스들을 포함한다.

동적 바인딩

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

동적 메서드 탐색

객체지향 시스템은 다음 규칙에 따라 실행할 메서드를 선택한다.

  1. 메시지를 수신한 객체는 먼저 자신을 생성한 클래스에 적합한 메서드가 존재하는지 검사한다. 존재하면 메서드를 실행하고 탐색을 종료한다.

  2. 메서드를 찾지 못했다면 부모 클래스에서 메서드 탐색을 계속한다. 이 과정은 적합한 메서드를 찾을 때까지 상속 계층을 따라 올라가며 계속된다.

  3. 상속 계층의 가장 최상위 클래스에 이르렀지만 메서드를 발변하지 못한 경우 예외를 발생시키며 탐색을 중단한다.

메서드 탐색은 자식 클래스에서 부모 클래스 방향으로 진행된다. 따라서 자식 클래스에 선언된 메서드가 부모 클래스의 메서드보다 더 높은 우선순위를 가지게 된다.

동적 메서드 탐색은 두 가지 원리로 구성된다.

  • 자동 메시지 위임 : 자식 클래스는 자신이 이해할 수 없는 메시지를 전송받은 경우 상속 계층을 따라 부모 클래스에게 처리를 위임한다.
  • 동적인 문맥 : 메시지를 수신했을 때 실제로 어떤 메서드를 실행할지를 결정하는 것은 컴파일 시점이 아닌 실행 시점에 결정된다.

self 참조

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

C++, C#, Java에서는 this라고 부른다.

super 참조

자식 클래스에서 부모 클래스의 구현을 재사용해야 하는 경우가 있다. 자식 클래스에서 부모 클래스의 변수나 메서드에 접근하기 위해 사용할 수 있는 super 참조라는 내부 변수를 제공한다.

super 변수를 통해 호출되는 메서드는 부모 클래스의 메서드가 라니라 더 상위에 위치한 조상 클래스의 메서드일 수도 있다. 이것은 super 참조를 통해 실행하고자 하는 메서드가 반드시 부모 클래스에 위치하지 않아도 되는 유연성을 제공한다.

Reference

오브젝트, 코드로 이해하는 객체지향 설계

카테고리:

업데이트:

댓글남기기