我不了解动态绑定和适当覆盖的概念:
以下是一些代码:
class Cake {
public void taste (Cake c) {
System.out.println("In taste of Cake class");
}
}
class ChocolateCake extends Cake {
public void taste(Cake c) {
System.out.println("In taste (Cake version) of ChocolateCake class");
}
public void taste(ChocolateCake cc) {
System.out.println("In taste (ChocolateCake version) of ChocolateCake class");
}
}
public static void main(String[] args)
{
ChocolateCake cc = new ChocolateCake();
Cake c = new ChocolateCake();
Cake c1 = new Cake();
Cake c2 = new ChocolateCake();
c1.taste(cc);
c1.taste(c);
c2.taste(cc);
c2.taste(c);
}
我期望:
In taste of Cake class
In taste of Cake class
In taste (ChocolateCake version) of ChocolateCake class" <----
In taste (Cake version) of ChocolateCake class
实际:
In taste of Cake class
In taste of Cake class
In taste (Cake version) of ChocolateCake class <----
In taste (Cake version) of ChocolateCake class
如果对象的类型为ChocolateCake,并且我将cc也称为ChocolateCake,那么编译器如何显示它以Cake作为参数?
答案 0 :(得分:9)
这是因为Java在这种情况下同时使用了静态和动态绑定来选择要调用的方法。
有问题的一行是这个,对吧?
c2.taste(cc);
编译器首先选择要调用的方法(静态绑定)。由于c2
的编译时类型为Cake
,因此编译器仅看到taste(Cake)
方法。因此它说“呼叫taste(Cake)
”。
现在,在运行时,运行时需要根据taste(Cake)
的运行时类型来选择要调用的c2
的实现。这是动态绑定。是否选择Cake
中的一个?还是ChocolateCake
中的一个?由于c2
的运行时类型为ChocolateCake
,因此它将在taste(Cake)
中调用ChocolateCake
的实现。
如您所见,您甚至没有提到您以为将被称为taste(ChocolateCake)
的方法!这是因为这是taste
方法的不同重载,并且因为它在ChocolateCake
类中,编译器无法看到。编译器为什么看不到?因为c2
的编译时间类型为Cake
。
简而言之,编译器决定哪个重载,运行时决定哪个实现。
回应您的声明:
如果对象的类型为ChocolateCake ...
只有您知道对象的类型为ChocolateCake
。编译器没有。它只知道c2
的类型为Cake
,因为它的声明就是这样。
答案 1 :(得分:8)
由于c2
变量的引用类型为Cake
,因此将调用具有taste
类型参数的Cake
方法。
这是因为Cake
类型没有采用taste
实例的ChocolateCake
方法,所以您不能从Cake
类型引用中调用该方法变量。
现在,在Java中,由于运行时多态性的机制,正在调用taste
的重写ChocolateCake
方法,而不是在父Cake
类中声明的版本。这是由于实际上在运行时将检查Cake
引用所指向的对象,并将调用该特定实例的taste
版本。
因此,由于这两种效果的结合,您将看到该输出。
如果将c2
的引用类型更改为ChocolateCake
,您将看到输出为:
In taste (ChocolateCake version) of ChocolateCake class
在调用c2.taste(cc);
时,由于现在编译器和运行时都同意特别调用该taste(ChocolateCake cc)
方法。
答案 2 :(得分:0)
在Java中,根据c2.taste(cc)
的编译时类型,在编译时执行c2
情况下调用哪种方法集的决定。 c2
的编译时类型为Cake
,这意味着对c2
的 any 方法调用仅搜索类Cake
及其超类,并且不会搜索Cake
(即ChocolateCake
)的任何子类,即使所有子类对编译器都是可见的。
基于接收器和参数的实际运行时类型在运行时执行完全动态方法解析的语言很少会导致c2.taste(cc)
解析为ChocolateCake.taste(ChocolateCake cc)
,因为它会对运行时性能产生负面影响。