簡體   English   中英

關於Java重載和動態綁定的問題

[英]Question about Java overloading & dynamic binding

在下面的代碼中,第一個和第二個打印語句如何打印出SubObj? 頂部和子指向同一個Sub類嗎?

class Top {
    public String f(Object o) {return "Top";}
}

class Sub extends Top {
    public String f(String s) {return "Sub";}
    public String f(Object o) {return "SubObj";}
}

public class Test {
    public static void main(String[] args) {  
        Sub sub = new Sub();
        Top top = sub;
        String str = "Something";
        Object obj = str;


        System.out.println(top.f(obj));
        System.out.println(top.f(str));
        System.out.println(sub.f(obj));
        System.out.println(sub.f(str));
    }
}

以上代碼返回以下結果。

SubObj
SubObj
SubObj
Sub

既然您已經了解了案例1,3和4,那么讓我們解決案例2。

(請注意 - 我絕不是JVM或編譯器內部工作方面的專家,但這是我理解它的方式。如果有人讀這篇文章是JVM專家,請隨時編輯這個答案,你可能會發現任何差異。)

子類中具有相同名稱但簽名不同的方法稱為方法重載。 方法重載使用靜態綁定,這基本上意味着在編譯時強制適當的方法被“選擇”(即綁定)。 編譯器不知道對象的運行時類型(也就是實際類型)。 所以當你寫:

                         // Reference Type  // Actual Type
    Sub sub = new Sub(); // Sub                Sub
    Top top = sub;       // Top                Sub

編譯器只“知道”top是Top類型(也就是引用類型)。 所以當你后來寫:

    System.out.println(top.f(str)); // Prints "subobj"

編譯器“看到”調用'top.f'作為引用Top類的f方法。 它“知道”str是String類型,它擴展了Object。 因此,1)調用'top.f'引用Top類的f方法,2)類Top中沒有f方法接受String參數,3)因為str是Object的子類,Top類的f方法是編譯時唯一有效的選擇。 因此編譯器隱式地將str向上轉換為其父類型Object,因此可以將其傳遞給Top的f方法。 (這與動態綁定形成對比,其中上述代碼行的類型解析將推遲到運行時,由JVM而不是編譯器解析。)

然后在運行時,在上面的代碼行中,top被JVM向下轉換為它的實際類型sub。 但是,參數str已由編譯器向上轉換為Object類型。 所以現在JVM必須在class sub中調用一個帶有Object類型參數的f方法。

因此,上面的代碼行打印“subobj”而不是“sub”。

有關另一個非常相似的示例,請參閱: Java動態綁定和方法覆蓋

更新:發現這篇關於JVM內部工作原理的詳細文章:

http://www.artima.com/underthehood/invocationP.html

我評論了你的代碼,以便更清楚地了解發生了什么:

class Top {
    public String f(Object o) {return "Top";}
}

class Sub extends Top {
    public String f(String s) {return "Sub";} // Overloading = No dynamic binding
    public String f(Object o) {return "SubObj";} // Overriding = Dynamic binding
}

public class Test {
    public static void main(String[] args) {  

                                  // Reference Type     Actual Type
        Sub sub = new Sub();      // Sub                Sub
        Top top = sub;            // Top                Sub
        String str = "Something"; // String             String
        Object obj = str;         // Object             String

                                        // At Compile-Time:      At Run-Time:
        // Dynamic Binding
        System.out.println(top.f(obj)); // Top.f (Object)   -->  Sub.f (Object)

        // Dynamic Binding
        System.out.println(top.f(str)); // Top.f (Object)   -->  Sub.f (Object)

        // Static Binding
        System.out.println(sub.f(obj)); // Sub.f (Object)        Sub.f (Object)

        // Static Binding
        System.out.println(sub.f(str)); // Sub.f (String)        Sub.f (String)
    }
}

這是因為Java中的所有方法調用都是虛擬的 (默認情況下)。

也就是說,分辨率從實際對象 (不是表達式類型 )開始,並“繼續”繼承鏈(根據實際對象類型 ),直到找到第一個匹配方法。 非虛方法將從表達式開始 (將方法標記為final是非虛擬的。)

但是, 確切的方法簽名是在編譯時確定的 (Java不支持多分派,單分派僅在運行時基於接收者對象而變化) - 這解釋了為什么Sub.f(String)導致“ Sub“,例如Top.f(String) ”綁定“匹配Top.f(Object)的方法,即使在Top類型的子類型上調用。 (這是在編譯時確定的最佳合格簽名)。 虛擬調度本身也是一樣的。

快樂的編碼。

這與對象的表觀類型有關。 在編譯時,Java根據您聲明對象的類型而不是您實例化的特定類型進行類型檢查。

你有一個類型Top與方法f(對象)。 所以當你說:

 System.out.println(top.f(obj));

Java編譯器只關心對象top是Top類型,唯一可用的方法是將Object作為參數。 在運行時,它然后調用實際實例化對象的f(Object)方法。

下一個調用以相同的方式解釋。

接下來的兩個調用將被解釋為您所期望的。

是的,他們都指向Sub類。 問題是top只知道

f(Object o)

它只能調用該簽名。

但是sub知道兩個簽名並且必須通過參數類型進行選擇。

在繼承中,基類對象可以引用派生類的實例。

這就是Top top = sub; 效果很好。

  1. 對於System.out.println(top.f(obj));

    top對象嘗試使用Sub類的f()方法。 現在Sub類中有兩個f()方法,對傳遞的參數進行類型檢查。 由於類型是Object因此調用Sub類的第二個f()方法。

  2. 對於System.out.println(top.f(str));

    您可以將其解釋為(1),即類型為String以便調用第一個f()函數。

  3. 對於System.out.println(sub.f(obj));

    這很簡單,因為您正在調用Sub類本身的方法。 現在因為Sub類中有兩個重載方法,所以這里也會對傳遞的參數進行類型檢查。 由於傳遞的參數是Object類型,因此調用第二個f()方法。

  4. 對於System.out.println(sub.f(str));

    與3.類似,這里傳遞的類型是String因此調用Sub類的第一個f()函數。

希望這可以幫助。

Sub sub = new Sub();
Top top = sub;

你創建了一個sub的實例,然后將它上傳到頂部,這使得它只知道頂部存在的方法。 top中存在的方法是public String f(Object o) {return "Top";}

現在,該方法也被sub重載,因此當您創建sub的實例並將其向上轉發時它將被調用。

另一種說法就是你得到了

sub type作為表觀類型,但top作為實際類型,因為你指定了sub to top。 如果它重載實際類型,你將調用表觀類型的方法,但你不能調用實際類型中不存在的任何方法

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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