백기선님 자바 기초 스터디 6주차 과제 질문을 참조하여 포스트를 작성하였습니다.
이번 포스트에서는 자바의 상속 - 다이나믹 메소드 디스패치에 대해 알아보겠습니다.
다이나믹 메소드 디스패치는 오버라이드 된 메소드에 대한 호출이 컴파일 타임이 아닌, 런타임에 결정되는 매커니즘입니다.
오버라이드 된 메소드가 부모 클래스 참조객체에 의해 호출될 때, 자바는 참조 변수의 유형이 아닌, 호출 시 참조되는 객체에 따라 실행되는 메소드의 버전(슈퍼/서브 클래스)를 결정합니다. 따라서, 이 결정은 런타임에 이루어집니다.
슈퍼클래스 참조 변수는 서브클래스 객체를 참조할 수 있으며 이를 업캐스팅이라고 합니다. 슈퍼클래스가 서브클래스에 의해 재정의되는 메서드를 포함하는 경우, 슈퍼클래스 참조 변수를 통해 서로 다른 유형의 개체를 참조할 때 서로 다른 버전의 메서드가 실행됩니다.
예시 코드를 보겠습니다.
// A Java program to illustrate Dynamic Method
// Dispatch using hierarchical inheritance
class A
{
void m1()
{
System.out.println("Inside A's m1 method");
}
}
class B extends A
{
// overriding m1()
void m1()
{
System.out.println("Inside B's m1 method");
}
}
class C extends A
{
// overriding m1()
void m1()
{
System.out.println("Inside C's m1 method");
}
}
// Driver class
class Dispatch
{
public static void main(String args[])
{
// object of type A
A a = new A();
// object of type B
B b = new B();
// object of type C
C c = new C();
// obtain a reference of type A
A ref;
// ref refers to an A object
ref = a;
// calling A's version of m1()
ref.m1();
// now ref refers to a B object
ref = b;
// calling B's version of m1()
ref.m1();
// now ref refers to a C object
ref = c;
// calling C's version of m1()
ref.m1();
}
}
Output:
Inside A's m1 method
Inside B's m1 method
Inside C's m1 method
위 프로그램은 A라는 슈퍼클래스와 2개의 서브 클래스(B, C)를 생성하고 있습니다. 그리고 서브클래스들은 m1()이라는 메소드를 오버라이드 하고 있습니다.
A a = new A(); // object of type A
B b = new B(); // object of type B
C c = new C(); // object of type C
A ref; // obtain a reference of type A
ref = a; // r refers to an A object
ref.m1(); // calling A's version of m1()
ref = b; // now r refers to a B object
ref.m1(); // calling B's version of m1()
ref = c; // now r refers to a C object
ref.m1(); // calling C's version of m1()
이처럼 A 타입의 ref
참조변수가 컴파일 시 타입(A)이 아닌, 런타임 시 참조되는 객체의 타입에 따라 오버라이드 된 메소드의 버전을 결정하는 것을 다이나믹 메소드 디스패치라고 부릅니다.
단, 주의할 점이 있습니다. 자바에서 변수가 아닌 메소드만 오버라이드 가능한 것 처럼, 런타임 다형성은 데이터 멤버 변수에서는 보장되지 않는다는 것입니다.
예를 들어보겠습니다.
// Java program to illustrate the fact that
// runtime polymorphism cannot be achieved
// by data members
// class A
class A
{
int x = 10;
}
// class B
class B extends A
{
int x = 20;
}
// Driver class
public class Test
{
public static void main(String args[])
{
A a = new B(); // object of type B
// Data member of class A will be accessed
System.out.println(a.x);
}
}
Output:
10
A타입의 참조변수 a
에, B 객체를 할당한 후 a
변수의 x
값을 출력하는 프로그램입니다.
B 객체를 할당하였기 때문에 20이 출력될 것으로 예상되었지만, 예상과 다르게 10이 출력되었습니다.
그러나 변수는 오버라이드 되지 않기 때문에, a.x
는 항상 슈퍼 클래스의 데이터 변수를 참조하게 됩니다. 따라서 10이 출력되는 것입니다.
다이나믹 메소드 디스패치는 자바가 런타임 다형성의 키 포인트인 메소드 오버라이딩을 지원할 수 있도록 합니다.
이 방법을 사용하면 한 클래스가 모든 하위 클래스에 공통되는 메소드를 지정할 수 있으며, 하위 클래스는 메소드의 일부 또는 전체를 새롭게 구현할 수 있습니다(오버라이딩).
하위 클래스가 특정 메서드를 추가하여 몇몇의 특정적인 메소드를 구현할 수 있습니다.
정답은 아니오 입니다.
컴파일 타임에서 컴파일러는 슈퍼클래스가 가지고 있는 메소드들만 알고 있는 상태입니다.
런타임 시에 참조되는 객체의 타입에 따라 메소드의 버전이 결정된다 하더라도, 컴파일 시에 슈퍼클래스가 해당 메소드를 가지고 있지 않다면 메소드를 찾을 수 없다는 에러 메세지가 발생하게 됩니다.
예시 코드를 보겠습니다.
class A
{}
class B extends A
{
// new method m1()
void m1()
{
System.out.println("Inside B's m1 method");
}
}
class Dispatch
{
public static void main(String args[])
{
// obtain a reference of type A
A ref;
// now ref refers to a B object
ref = new B();
// calling B's version of m1()
ref.m1();
}
}
Output:
다이나믹 메소드 디스패치는 꼭 오버라이드 된 메소드에 대해서 적용된다는 점을 인지하면 좋을 것 같습니다🙂👍