2010-04-09 21 views
3
class A   { void F() { System.out.println("a"); }} 
class B extends A { void F() { System.out.println("b"); }} 

public class X { 
    public static void main(String[] args) { 
     A objA = new B(); 
     objA.F(); 
    } 
} 

這裏,F()被動態調用,不是嗎?Java是否支持動態方法調用?

This article說:

...的Java字節碼不支持 動態方法調用。有 三個受支持的調用模式: invokestatic,invokespecial, invokeinterface或invokevirtual。 這些模式允許調用具有已知簽名的方法 。我們談論 強類型語言。這允許 直接在 編譯時進行一些檢查。

另一方面,動態的 語言使用動態類型。因此,我們可以在編譯 時調用未知的方法 ,但用Java字節碼完全不可能得到 。

我在想什麼?

+0

運行該程序時會發生什麼? – 2010-04-09 16:46:47

+0

@Dave:打印'b' – Lazer 2010-04-09 16:52:18

+1

該文章的作者需要計算一課。 – Syntactic 2010-04-09 16:56:16

回答

13

你感到困惑動態調用動態綁定 ..

第一個允許類型檢查接受你不能確定程序,如果一個方法是存在的物體上時運行時間,而動態綁定只是根據對象的運行時類型選擇正確的實現,但保持靜態類型檢查

這是什麼意思?

這意味着在您的示例中,Java將調用對象B上的實現,因爲objA變量的運行時類型爲B;並且它會進行編譯,因爲它知道一個B是一個A因此方法調用在運行時不會失敗(當然objA將有一個F實現)。

使用動態調用,而不會在編譯時檢查調用的對象的類型F是否包含該方法,當然如果在執行期間該方法不可用,則會引發異常指定的對象。

僅供參考:invokedynamic功能將與Java7一起添加,因爲許多腳本語言已被編寫爲可在JVM之上工作,並且缺少動態調用功能迫使這些語言的開發人員在腳本以及使用反射來關注動態調用的真正JVM。當然這種方法會導致很多開銷(想想Grovvy的MetaClass),這就是爲什麼Sun決定給他們一個幫助。

+0

爲瑣事+1 – 2010-04-09 23:59:46

1

在你的例子中,正確的方法被調用,因爲多態的B實例看起來像A.可以通過檢查對象的運行時類型來定位該方法;也就是B;這與對象引用的編譯時類型相反。另一個重要部分是方法的簽名 - 它們必須始終匹配(當然是多態)。

這與動態語言不同,因爲在那些對象中基本上沒有編譯時間 - 並且所有事情都必須在運行時解決。

1

事實上,你錯過的是這是在文章中解釋的'invokevirtual'的一部分。

你只是重寫方法,並使用虛擬方法表來調用正確的方法。

0

我不會打電話給你的例子「dynamic」,而是虛擬。因爲在編譯時,方法名稱和簽名是已知的(並且它的存在由編譯器檢查)。唯一在運行時解析的是用於該方法的具體實現。

「動態」方法調用的一個更合適的例子將涉及反射,(請參閱Method類)。這樣,可以在運行時調用其存在未知的編譯類型的方法(這被框架廣泛使用,而不是應用程序代碼)。

你提到的文章在這方面似乎有點誤導。但是確實,你明確調用的方法的簽名必須在編譯時被知道/檢查,所以在這個意義上,Java不是動態的。

0

您可以製作功能接口。

class Logger { 
    private BiConsumer<Object, Integer> logger = null; 

    // ... 

    private Logger(Object logger) { 
     this.akkaLogger = (LoggingAdapter) logger; 
     this.logger = (message, level) -> { 
      switch (level) { 
       case INFO: akkaInfo(message); 
          break; 
       case DEBUG: akkaDebug(message); 
          break; 
       case ERROR: akkaError(message); 
          break; 
       case WARN: akkaWarn(message); 
          break; 
      } 
     }; 
    } 

    private Logger() { 
     this.logger = (message, level) -> System.out.println(message); 
    } 

    // ... 
}