2011-04-14 52 views
14

在下面的代碼中,第一個和第二個打印語句如何打印出SubObj? 做頂部和子指向相同的子類?關於Java重載和動態綁定的問題

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 
+0

現在我得到了第一行的工作方式,但第二行怎麼會打印出SubObj,即使在把調用是top.f(str)其中str是一種類型的字符串? – user482594 2011-04-14 05:05:04

+0

我發佈了一個答案,你看過它。它應該解決你的疑惑。總結一下從「類型檢查」的角度來考慮傳遞的參數。請接受答案,如果你覺得有幫助.. – 2011-04-14 06:35:26

回答

10

既然你已經瞭解情況1,3,4,讓我們來解決的情況下2.

(請注意 - 我絕不是在JVM或編譯器的內部工作的專家,但是這是怎麼我明白了,如果有人讀這是一個JVM專家,請隨時編輯這個答案,你可能會發現任何差異。)

一個名稱相同但簽名不同的子類中的方法稱爲方法重載。方法重載使用靜態綁定,這基本上意味着在編譯時將強制適當的方法被「選擇」(即綁定)。編譯器不知道對象的運行時類型(又名實際類型)。所以,當你寫:

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

編譯器只「知道」,頂部是頂部類型(又名引用類型)。所以當你後來寫道:

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

編譯器「看到」調用'top.f'作爲引用Top類的f方法。它「知道」str是擴展Object的String類型。因此,1)調用'top.f'是指Top類的f方法,2)Top類中沒有f方法接受String參數,3)由於str是Object的子類,Top類的f方法是編譯時唯一有效的選擇。所以編譯器隱式地將str上傳到它的父類型Object,所以它可以傳遞給Top的f方法。 (這與動態綁定相反,其中上述代碼行的類型解析將被推遲到運行時,由JVM而不是編譯器來解決)。

然後在運行時,在上面的代碼行中,頂層被JVM貶低爲實際類型sub。但是,參數str已經被編譯器強制轉換爲Object類型。所以現在JVM必須在類sub中調用一個f方法,該方法接受一個Object類型的參數。

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

對於另一個非常類似的例子,請參閱:Java dynamic binding and method overriding

更新:發現了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) 
    } 
} 
1

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

f(Object o) 

,它可以調用不僅對簽名​​。

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

6

這是因爲在Java中所有方法調用都virtual(默認)。

即,分辨率開始於實際對象(不是型表達的)和「作品向上」繼承鏈(每的實際對象鍵入),直到第一匹配方法被發現。非虛擬方法將從類型的表達式開始。 (標記的方法作爲final使得非虛。)

然而,確切方法簽名在編譯時決定(Java不支持多調度,單分派僅在遊程而變化基於接收者對象的時間) - 這就解釋了爲什麼Sub.f(String)導致「Sub」,例如Top.f(String)「綁定」到匹配Top.f(Object)的方法,即使調用Top子類型也是如此。 (這是在編譯時確定的最佳合格簽名)。虛擬調度本身是相同的。

快樂編碼。

2

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

您有一個類型爲Top的方法f(Object)。所以當你說:

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

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

下一個呼叫以相同的方式解釋。

接下來的兩個調用按照您的預期進行解釋。

0

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

你言子的一個實例,然後由澆鑄它到頂部,這使得它僅知道存在於頂部的方法。 存在於頂端的方法是公開的String f(Object o) {return "Top";}

現在該方法也被sub重載,所以當你創建一個sub的實例並將它上傳到頂層時,它將被調用。

另一種方式把這是你有

子類型爲明顯的類型,但頂部的實際類型,因爲您分配子頂部。 如果它將過載實際類型,但您將無法調用實際類型中不存在的任何方法,則將調用明顯類型的方法

1

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

這就是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()函數被調用。

希望這會有所幫助。

+0

對於第二個,實際輸出是'SubObj'不是'Sub' – user482594 2011-04-14 13:50:56