[英]Calling overloaded inherited methods using super class reference
我不明白這種Java行為。 我有兩節課:
class C1 {
public void m1(double num) {
System.out.println("Inside C1.m1(): " + num);
}
}
class C2 extends C1 {
public void m1(int num) {
System.out.println("Inside C2.m1(): " + num);
}
}
這是我的主要內容:
public class Main {
public static void main(String[] args) {
C1 c = new C2();
c.m1(10);
}
}
結果是:
Inside C1.m1(): 10.0
當我預料到:
Inside C2.m1(): 10
當我嘗試完成代碼語法時,我發現了這個:
C2類的其他m1在哪里?
我還檢查了我的Main.class的字節碼,我看到了這個:
Compiled from "Main.java"
public class com.company.Main {
public com.company.Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class com/company/C2
3: dup
4: invokespecial #3 // Method com/company/C2."<init>":()V
7: astore_1
8: aload_1
9: ldc2_w #4 // double 10.0d
12: invokevirtual #6 // Method com/company/C1.m1:(D)V
15: return
}
字節碼告訴我它將調用C1.m1(D)V(第12行)。
為什么C1的方法? 我試圖理解這種行為。
你的兩個名為m1
方法沒有相同的簽名; 超類中的一個采用double
,而子類中的一個采用int
。 這意味着編譯器將根據變量的編譯時類型(即C1
選擇要調用的方法簽名,並調用m1(double)
。 由於在運行時C2
類沒有覆蓋版本的m1(double)
,因此調用C1
的版本。
規則是在編譯時基於編譯時類型計算方法簽名 ; 方法調用在運行時根據匹配的簽名進行調度 。
這是因為參數。 您調用的方法是具有double參數的方法。 C2內部的m1沒有覆蓋它,而是重載它。
如果要在C2中調用m1,則必須轉換引用,以便編譯器接受您正在執行的操作。
你看到輸出為Inside C1.m1(): 10.0
而不是Inside C1.m1(): 10
或Inside C2.m1(): 10.0
的原因是因為:
C2
的方法m1
。 您正在將從C1
繼承的m1(doube)
方法重載到m1(int)
。 C2
類現在有兩個m1
方法。 一個inherited
自C1
並具有簽名m1(double)
和一個在C2
重載並具有簽名m1(int)
c.m1(10)
,它會根據引用類型解析此調用。 由於引用類型是C1
,編譯器將在C1
將此調用解析為m1(double)
。 C2
m1(double)
的調用,這是從C1
繼承的方法。 (如第2點所述) 有兩種方法可以調用m1(int)
方法:
((C2)c).m1(10);
要么
C2 c = new C2(); c.m1(10);
Java對靜態類型執行方法調度,並且您的變量c
的類型為C1
,因此m1(int)
不可見,並且您的10
被強制轉換為double
。
兩種方法的方法簽名是不同的。
public void m1(double num)
public void m1(int num)
所以在這種情況下沒有壓倒一切。 現在,當你說
C1 c = new C2();
c.m1(10);
在編譯時,它將引用類型C1
,它具有方法public void m1(double num)
,它與10 [int in expanded to double]兼容。 因此int被提升為double,並調用相應的方法(這也是您在字節碼中看到的)。
如果你調用c.m1(10.0),它會像你原先想象的那樣調用祖先的方法。
你正在你的例子中進行方法重載(即添加更多具有相同名稱和不同簽名的方法)而不是方法覆蓋(即通過使用相同的簽名重新聲明它在后代上改變祖先方法的實現,AKA相同名稱和相同類型的結果以及方法參數 - 參數名稱無關緊要)。
通過查看您的代碼,您沒有利用繼承來獲得您想要的答案。 你必須改變這一行
C1 c = new C2();
至
C2 c = new C2();
您無法看到C2的方法,因為您將實例變量聲明為C1,也因為它們沒有相同的簽名。 你在一個方法中有雙參數,第二個是int類型,這使得它們對於JVM完全不同的方法(因此這里沒有繼承)。
因此,如果你在C1方法中有int類型,那么你需要在C2方法中也有int類型,然后JVM將像你想要的那樣從C2運行方法。
您也可以將變量轉換為C2類型,然后您就可以訪問C2的方法。
由於C1.m1(double num)
是一個公共方法,它繼承了C2。 所以你的C2也有一個方法, m1(double num)
,這就是它被調用的原因。 從main()
你實際上調用了C2.m1(double num)
。
注意 :現在在C2
類,你有兩個重載方法 - m1(int num)
和m1(double num)
。 C2.m1(int num)
是與C2.m1(double num)
不同的方法。
Java選擇最具體的適用類型。 在這種情況下,m1(int)不適用。 強調包含相同類(c1)的對象的類的引用或類(c2)的任何子類的對象以及方法名稱和參數列表。
正在調用具有double參數的方法,因為double優先於int。 這是因為int可以分配給double,但不是相反。
因此,在方法調用的(運行)時間有很多事情需要考慮。
是的,對於你的情況,你的主要課程應該是這樣的
public static void main(String[] args) {
C1 c = new C2();
c.m1(10);
((C2) c).m1(10);
//or
C2 cobj = new C2();
cobj.m1(10);
}
**OutPut**
Inside C1.m1(): 10.0
Inside C2.m1(): 10
Inside C2.m1(): 10
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.