我正在阅读一本JAVA书,并遇到了动态方法调度。但这对我来说有点混乱(也许是因为我是个新人)。该书说它基于以下原则:超类引用变量可以引用子类对象。
class X{
void display()
{
System.out.println("This is class X");
}
}
class Y extends X{
void display()
{
System.out.println("This is class Y");
}
void play()
{
System.out.println("PLAY!");
}
}
class k{
public static void main(String args[]){
X obj1 = new X();
Y obj2 = new Y();
X ref = new X();
ref = obj1;
ref.display();
//output is :This is class X
ref = obj2; //Using the principle stated above
ref.display();
//output is :This is class Y
ref.play(); //Compiler error:Play not found
//well it must be because ref is of X type and for X no methods of its subclass "Y"
//is visible
}
}
所以我想问一下 玩() 为什么不可见 显示() Y是可见的吗?
答案 0 :(得分:1)
根据 promises 来考虑它。 X ref
承诺它拥有的内容(如果有的话)属于X
类型。当然,Y
也属于X
类型,这就是子类化的含义:Y
的每个实例都是X
的一个实例(加上一些调整和额外的特征)。但是,根据语言定义,无法通过它调用play
方法,因为内容不是已知是Y
。
您可以明确地 ref
到Y
,然后在其上调用play
:
((Y) ref).play();
这是安全的,因为Java中的每个对象都知道自己的真实类型。如果对象引用是错误类型的实例(例如,obj1
),则在运行代码时将获得ClassCastException
。 (如果你知道C,这完全不同。)
答案 1 :(得分:0)
父类引用包含从父级继承的子对象的属性。因此,您可以从包含子对象的父引用调用所有这些方法,这些子对象由子类继承并覆盖。
简而言之,引用将决定可以调用哪些方法,它所持有的对象将决定哪个方法(父/子类)将被调用。
所以我想问一下,如果play()不可见,那么为什么要显示() Y是可见的??
显示是可见的,因为这是父参考的方法。但是游戏只属于孩子,因此父母看不到。
答案 2 :(得分:0)
静态类型检查确保您只能调用那些属于引用的静态(声明)类型的方法。这就是为什么你不能通过类型为X的引用调用.play()。
但是,动态方法分派确保,如果在子类中重写方法,则将动态调用该特定方法(在运行时)。
答案 3 :(得分:0)
在执行之前,Java程序由编译器处理。编译器确保在许多其他方面只调用现有方法。在上面的例子中,编译器只知道 ref 是 X 类型,如声明的那样,并且 X 类型没有方法播放。它不会跟踪所有分配,也不会考虑 ref 是由 y 类型的 obj2 分配的 - 这种跟踪通常是不可能的case,因为它可能依赖于运行时数据,在编译时不可用。
另一方面,运行时的方法调用依赖于实际类型,它可以是声明类型的扩展。编译器发出的完全相同的代码可以调用方法 display 的不同实现,具体取决于变量 ref 引用的对象的实际类型。但是,编译器检查可能分配给 ref 的所有可能类型的对象存在方法 display 。