我知道在覆盖方法的情况下,Java遵循动态绑定。但是,如果我们从父引用变量(引用子对象)调用仅子方法,则会出现编译错误。
为什么Java遵循这种设计(即为什么在第二种情况下没有动态绑定)?
class A{
public void sayHi(){ "Hi from A"; }
}
class B extends A{
public void sayHi(){ "Hi from B";
public void sayGoodBye(){ "Bye from B"; }
}
main(){
A a = new B();
//Works because the sayHi() method is declared in A and overridden in B. In this case
//the B version will execute, but it can be called even if the variable is declared to
//be type 'A' because sayHi() is part of type A's API and all subTypes will have
//that method
a.sayHi();
//Compile error because 'a' is declared to be of type 'A' which doesn't have the
//sayGoodBye method as part of its API
a.sayGoodBye();
// Works as long as the object pointed to by the a variable is an instanceof B. This is
// because the cast explicitly tells the compiler it is a 'B' instance
((B)a).sayGoodBye();
}
在方法调用的动态调度开始操作之前,代码必须通过编译器。当你使用类C
的引用调用方法时,编译器会在该类C
中查找该方法的声明。编译器只担心它是什么引用类型。它只能验证对这么多信息的方法调用。如果它在类 C
中找不到该方法,它将给你编译器错误。
因此,对于调用:
a.sayGoodBye();
由于a
是类A
的引用,编译器将在类A
中查找该方法,如果找不到,则会给出编译器错误。
B
是一个A
,但A
不一定是B
。所以B
的方法不能在引用A
调用
可以这样想:当 B 扩展 A 时;这意味着
B = <All non-private stuff of A> + Its own stuff
这意味着 B 拥有使用 A 的所有特权(就像 A 成为 B 的子集一样)
但是A从来不知道B在这里以及B包含什么;所以,它不能使用B的东西。如果以某种方式需要强制 A 表现得像 B(我们使用 cast),因为这里A is not B
;转换将在运行时失败。
要了解有关 Java 强制转换的更多信息,请阅读此线程