簡體   English   中英

使用超類引用調用重載的繼承方法

[英]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(): 10Inside C2.m1(): 10.0的原因是因為:

  1. 您沒有覆蓋C2的方法m1 您正在將從C1繼承的m1(doube)方法重載到m1(int)
  2. C2類現在有兩個m1方法。 一個inheritedC1並具有簽名m1(double)和一個在C2重載並具有簽名m1(int)
  3. 當編譯器看到調用c.m1(10) ,它會根據引用類型解析此調用。 由於引用類型是C1 ,編譯器將在C1將此調用解析為m1(double)
  4. 在運行時,JVM將解析對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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM