Java中的动态方法分派或运行时多态性
先决条件:在Java中覆盖,继承
方法覆盖是Java支持运行时多态的方式之一。动态方法分派是在运行时而不是编译时解决对重写方法的调用的机制。
- 当通过超类引用调用被覆盖的方法时, Java会根据调用发生时引用的对象的类型来确定要执行该方法的哪个版本(超类/子类)。因此,该确定是在运行时做出的。
- 在运行时,它取决于被引用对象的类型(而不是引用变量的类型),它决定了将执行哪个版本的覆盖方法
- 超类引用变量可以引用子类对象。这也称为向上转换。 Java使用这个事实来解决在运行时对覆盖方法的调用。
因此,如果超类包含被子类覆盖的方法,那么当通过超类引用变量引用不同类型的对象时,会执行不同版本的方法。这是一个说明动态方法分派的示例:
// 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();
}
}
输出:
Inside A's m1 method
Inside B's m1 method
Inside C's m1 method
解释 :
上面的程序创建了一个名为 A 的超类,它是两个子类 B 和 C。这些子类覆盖了 m1( ) 方法。
- 在 Dispatch 类的 main() 方法中,最初声明了 A、B 和 C 类型的对象。
A a = new A(); // object of type A B b = new B(); // object of type B C c = new C(); // object of type C
- 现在还声明了一个名为 ref 的类型 A 的引用,最初它将指向 null。
A ref; // obtain a reference of type A
- 现在我们将对每种类型的对象(A 或 B 或 C)的引用一个接一个地分配给ref ,并使用该引用来调用 m1( )。如输出所示,执行的 m1( ) 版本由调用时引用的对象类型决定。
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()
具有数据成员的运行时多态性
在Java中,我们只能覆盖方法,不能覆盖变量(数据成员),因此数据成员无法实现运行时多态性。例如 :
// 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);
}
}
输出:
10
解释:在上面的程序中,类 A(超类)和 B(子类)都有一个公共变量“x”。现在我们创建类 B 的对象,由 A 类类型的 'a' 引用。由于变量没有被覆盖,所以语句“ax”将始终引用超类的数据成员。
动态方法调度的优点
- 动态方法分派允许Java支持对运行时多态性至关重要的方法的覆盖。
- 它允许一个类指定对其所有派生类都通用的方法,同时允许子类定义部分或所有这些方法的特定实现。
- 它还允许子类添加其特定的方法子类来定义一些特定的实现。
静态与动态绑定
- 静态绑定是在编译时完成的,而动态绑定是在运行时完成的。
- 私有、最终和静态方法和变量使用静态绑定并由编译器绑定,而覆盖的方法在运行时根据运行时对象的类型绑定