[英]Java dynamic binding and method overriding process
我有點知道靜態綁定在編譯時發生而動態綁定在運行時發生的原理。 我已經閱讀了幾個相關的問題。 我可以遵循其中許多人的思路,但是當我遇到如下具體問題時,我又陷入混亂並失去了邏輯:
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");
}
}
class BirthdayCake extends ChocolateCake {
public void taste(Cake c) {
System.out.println("In taste (Cake version) of BirthdayCake class");
}
public void taste (ChocolateCake cc) {
System.out.println("In taste (ChocolateCake version) of BirthdayCake class");
}
public void taste(BirthdayCake bc) {
System.out.println("In taste (BirthdayCake version) of BirthdayCake class");
}
}
已創建以下對象:
Cake c1 = new Cake();
ChocolateCake cc = new ChocolateCake();
Cake c2 = new ChocolateCake();
Cake c3 = new BirthdayCake();
輸出如下所示:
c1.taste(cc);//Output: In taste of Cake class
cc.taste(cc);//Output: In taste (ChocolateCake version) of ChocolateCake class
c2.taste(cc);//Output: In taste (Cake version) of ChocolateCake class
((BirthdayCake) c3).taste(cc);//Output: In taste (ChocolateCake version) of BirthdayCake class
((BirthdayCake) c3).taste((BirthdayCake) c3);//Output: In taste (BirthdayCake version) of BirthdayCake class
基本上,我的問題是為什么c2.taste(cc)
調用ChocolateCake
類中的c2.taste(cc)
taste(Cake c)
方法?
這是我的想法: c2
的靜態類型是Cake
,它決定了Cake
中的方法將被調用。 當涉及到運行時時,c2的動態類型(即ChocolateCake
)決定將調用ChocolateCake
cake中的方法。 並且由於其參數類型為ChocolateCake
,因此最終將調用taste(ChocolateCake cc)
。
顯然,這種想法是錯誤的。 如果我假設該方法的簽名在編譯時已確定下來,因為c2
的靜態類型為Cake
而Cake
類中只有一個方法。 當涉及到運行時時,它將調用ChocolateCake
類中的重寫方法。 我的困惑是為什么它以這種方式而不是以前的方式工作?
我不明白的另一件事是,我們不允許編寫如下語句,因為它會導致編譯錯誤:
ChocolateCake cc = new Cake();
。
但是為什么ChocolateCake類型引用最終可以傳遞Cake對象,因為它應該調用ChocolateCake
類中的ChocolateCake
taste(Cake c)
方法來獲得如上所述的正確輸出。
我認為我仍然不了解在對象引用上調用方法的整個過程。 就像在編譯時確定最佳匹配方法時發生的情況以及此后發生的情況(假設運行時)一樣(我不確定此過程中是否還有其他階段)。
誰能幫助說明這個過程? 非常感謝!
讓我嘗試簡化示例並逐步進行操作。 為了清楚起見,我還添加了@Override。
class Cake {
public void taste (Cake c) {
System.out.println("In taste of Cake class");
}
}
class ChocolateCake extends Cake {
@Override
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");
}
}
ChocolateCake param = new ChocolateCake();
Cake cake = new ChocolateCake();
cake.taste(param);
當您調用cake.taste(param);
Java編譯器根據引用的類型而不是引用所指向的實際對象類型來選擇要調用哪個方法的編譯時間。
的引用類型cake
是Cake
,所以編譯器看起來在基類Cake
要被調用方法taste
,並接受Cake
作為參數。 由於ChocolateCake
是Cake
(通過繼承),因此編譯器會找到匹配項。
因為從本質上來說,您有基本taste
方法的替代,所以在運行時以及由於動態調度,JVM會解析cake
引用的實際類型,即ChocolateType
並調用已選擇方法的替代。
我已經嘗試使用以下main():
public static void main(String[] args)
{
ChocolateCake cc = new ChocolateCake();
Cake c = new ChocolateCake();
Cake c1 = new Cake();
Cake c2 = new ChocolateCake();
Cake c3 = new BirthdayCake();
ChocolateCake c4 = new BirthdayCake();
c1.taste(cc);
c1.taste(c);
c2.taste(cc);
c2.taste(c);
c3.taste(cc);
c3.taste(c);
c4.taste(cc);
c4.taste(c);
}
輸出是這樣的:
In taste of Cake class
In taste of Cake class
In taste (Cake version) of ChocolateCake class
In taste (Cake version) of ChocolateCake class
In taste (Cake version) of BirthdayCake class
In taste (Cake version) of BirthdayCake class
In taste (ChocolateCake version) of BirthdayCake class
In taste (Cake version) of BirthdayCake class
到目前為止,我的理解如下:
例如
c4.taste(cc);//c4:ChocolateCake cc:ChocolateCake -> ChocolateCake version
c4.taste(c);//c4:ChocolateCake c:Cake -> Cake version
只是一些自己的理解,對Java也很新;)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.