개발 서적 기록/오브젝트_조영호

31일차 - 업캐스팅 & 동적 바인딩 그리고 동적 메서드 탐색

밍 끄적 2023. 9. 11. 22:32
728x90

2023.09.11 MON

401p ~ 415p

 

30일차 내용 ⬇️

2023.09.10 - [개발 서적 기록/오브젝트_조영호] - 30일차 - 다형성의 종류

 

30일차 - 다형성의 종류

2023.09.10 SUN 390p ~ 402p 29일차 내용 ⬇️ 2023.09.08 - [개발 서적 기록/오브젝트_조영호] - 29일차 - 믹스인을 통해서 상속 대체하기 29일차 - 믹스인을 통해서 상속 대체하기 2023.09.08 FRI 376p ~ 392p 28일차

magenta-ming.tistory.com


관점에 따른 상속

상속의 개념은 데이터의 관점에서 글고 행동 관점에서 분류할 수 있다.

 

데이터 관점의 상속

자식 클래스의 인스턴스 안에 부모 클래스의 인스턴스를 포함한다.

자식 클래스의 인스턴스는 자동으로 부모 클래스에서 정의한 모든 인스턴스 변수를 내부에 포함한다.

 

행동 관점의 상속

부모 클래스가 정의한 일부 메서드를 자식 클래스의 메서드로 포함시킨다.

부모 클래스의 모든 퍼블릭 메서드는 자식 클래스의 퍼블릭 인터페이스에 포함되므로, 외부의 객체가 부모 클래스의 인스턴스에게 전송할 수 있는 모든 메시지는 자식 클래스의 인스턴스에도 전송할 수 있다.

 

메서드를 상속받는 행위는 메모리적으로 이득이 될 수 있다.

데이터에 대해서, 서로 다른 상태를 저장할 수 있도록 각 인스턴스별로 독립적인 메모리를 할당받아야한다.

반면에, 메서드는 동일한 클래스의 인스턴스끼리 공유가 가능하므로 클래스는 한 번만 메모리에 로드하고 각 인스턴스 별로 클래스를 가리키는 포인터를 갖게 한다.


같은 메시지, 다른 메서드 : 업캐스팅 & 동적 바인딩

코드 안에서 선언된 참조 타입과 무관하게, 실제로 메시지를 수신하는 객체의 타입에 따라 실행되는 메서드가 달라질 수 있다.

이를 가능케 하는 메커니즘이 업캐스팅과 동적 바인딩이다.

이런 메커니즘을 통해서 코드를 변경하지 않고도 기능을 추가할 수 있기에, OCP 개방-폐쇄 원칙을 지키고 있다.

 

업캐스팅

부모 클래스 타입으로 변수를 선언했지만, 자식 클래스 인스턴스를 할당할 수 있다.

따라서 부모 클래스에 대해 작성된 코드를 전혀 수정하지 않고도, 자식 클래스에 적용할 수 있다.

 

업캐스팅은 두가지 방법으로 적용할 수 있다.

1. 참조 변수에 할당(대입) : 부모 클래스 타입의 참조 변수에 자식 클래스 인스턴스를 대입한다.

List<Integer> list = new LinkedList<>();

2. 파라미터에 전달 : 부모 클래스 타입으로 선언된 파라미터에 자식 클래스의 인스턴스를 전달한다.

public class Driving {
	public Driving(User user, Car car, Integer age) {...}
}

Driving driving = new Driving(myUser, new Hyundai(...), 23);

다운캐스팅

업캐스팅과 반대로 부모 클래스의 인스턴스를 자식 클래스 타입으로 변환하기 위해서 명시적으로 타입 캐스팅을 수행한다.

Car car = new Hyundai(...);
Hyundai hd = (Hyundai)car;

 

동적 바인딩 / 지연 바인딩

메시지를 수신하는 객체의 타입에 따라 실행되는 메서드가 결정된다.

선언된 변수의 타입과 관계 없이, 컴파일 시점이 아닌 실행 시점에 메시지를 처리할 적절한 메서드를 결정한다.

따라서 코드를 변경하지 않고도 실행되는 메서드를 변경할 수 있다.

 

동적바인딩과 달리 호출될 메서드를 컴파일 타임에 결정하는 메커니즘은 정적 바인딩 / 초기 바인딩 / 컴파일-타임 바인딩 이라고 부른다.


객체지향 시스템이 실행할 메서드를 선택하는 흐름 : 동적 메서드 탐색

실행하고자 하는 메서드를 찾는 과정은 런타임에 동적으로 수행된다.

구체적으로는 아래 과정을 따른다.

  1. 나 자신의 클래스 탐색 : 메시지를 수신한 객체는 먼저 자신을 생성한 클래스에 적합한 메서드가 존재하는지 검사한다. 
    1. 존재하면 메서드를 실행하고, 탐색을 종료한다.
  2. 부모 클래스 탐색 : 메서드를 찾지 못했다면 부모 클래스에서 메서드 탐색을 계속한다. 
    1. 적합한 메서드를 찾을 때까지, 상속 계층에 따라 계속 부모를 탐색한다.
  3. 어디에도 없다면 예외 처리 : 상속 계층의 가장 최상위 클래스에 이르렀지만 메서드를 발견하지 못한 경우 예외를 발생시키고, 탐색을 중단한다.

부모 클래스를 탐색하기 위해서는 참조가 필요하다 : self 참조

시스템이 실행할 메서드를 선택하기 위해서 부모 클래스를 탐색한다.

이때 부모 클래스를 탐색하기 위해서, 즉 상속 계층의 역방향으로 탐색하기 위해서는 해당 방향을 계속 가르키는 참조값이 필요하다.

이것이 self 참조 ( self reference ) 변수다.

 

객체가 메시지를 수신하면 컴파일러는 self 참조라는 임시 변수를 자동으로 생성한 후, 메시지를 수신한 객체를 가리키도록 설정한다.

동적 메서드 탐색은 self가 가리키는 객체의 클래스에서 시작해서 상속 계층의 역방향으로 이뤄지며, 메서드 탐색이 종료되는 순간 self 참조는 자동으로 소멸된다.

 

동적 메서드 탐색의 원리

원리 1 : 자동적인 메시지 위임

자식 클래스는 자신이 이해할 수 없는 메시지를 전송받은 경우 상속 계층을 따라 부모 클래스에게 처리를 위임한다.

클래스 사이의 위임은 프로그래머의 개입 없이 상속 계층을 따라 자동으로 이뤄진다.

 

원리 2 : 동적인 문맥 사용

메시지를 수신했을 때 실제로 어떤 메서드를 실행할지를 결정하는 것은 컴파일 시점이 아닌 실행 시점에 이뤄진다.

메서드를 탐색하는 경로는 self 참조를 이용해서 결정한다.

728x90