2012-03-13 51 views
4

我想知道爲什麼的Java:重載方法

z.f(-6); 

在M級的呼叫是指下列函數B類:的

public void f(double y) { 
    this.x = (int) y + B.y; 
} 

,而不是在A級使用函數f,因爲bx由A覆蓋。或者使用

public void f (int y) { 
    this.x = y*2; 
    B.y = this.x; 
} 

在類B中,至少參數類型匹配。

完成以下代碼:

public class A { 
    public int x = 1; 
    public A(int x) { 
     this.x += x; 
    } 
    public A (double x) { 
     x += x; 
    } 
    public void f(double x) { 
     this.x = this.x + (int) (x + B.y); 
    } 
} 

public class B extends A { 
    public static int y = 3; 
    public int x = 0; 
    public B (double x) { 
     super((int) x); 
    } 
    public void f(int y) { 
     this.x = y*2; 
     B.y = this.x; 
    } 
    public void f(double y) { 
     this.x = (int) y + B.y; 
    } 
    } 

public class M { 
     public static void main (String[] args){ 
     A a = new A(B.y); 
     a.f(1); 
     B b = new B(3.0); 
     A z = b; 
     z.f(-5.0); 
     z.f(-6); 
     System.out.println(b.x + " " + z.x); 
     } 
    } 
+0

我覺得Mike Samuel的回答很好,因爲z有類型A,儘管它擁有A的一個子類B的實例,但只有A的方法可以被調用;對於子類型B的方法的調用,你將不得不downcast z(儘管使用checked downcast,例如使用instanceof,因爲否則可能會遇到運行時異常)。 - 在一個不相干的筆記上,既然我看到了你對理論計算機科學的簡介,並且正在考慮在RWTH做我的主人,那麼在那裏學習怎麼樣?從您對TCS的問題來看,聽起來有些苛刻? – 2012-03-13 23:51:48

+0

編輯:我誤讀了麥克塞繆爾的答案,在他的第一句話,它需要閱讀「A.f(雙)」。一個正確的,不太詳細的答案(這也是不太需要,而仍然捕捉問題)將是悲慘變量的;也檢查那一條的評論。 – 2012-03-14 00:33:56

+0

@ G. Bach:我可以強烈推薦RWTH大學。這很有挑戰性,但很有趣。我只是在我的第一個學期,所以我的經驗是相當有限的;-) – Laura 2012-03-15 18:46:09

回答

3
z.f(-6); 

靜態類型的zA,它只有一個名爲f一種方法。該方法需要double參數,可以提升文字值-6。所以在編譯時,調用綁定到A.f(double)

在運行時z被發現是B類型,它有自己的B.f(double)覆蓋A.f(double)的,所以調用這一點,得到該方法。

+0

我的印象是,如果變量被轉換爲子類,調用只會綁定到子類方法 - 然而,一個簡單的測試表明,你是對的,即使在編譯時調用綁定到Af(double ),它在運行時綁定到Bf(double),因爲z是B類型的一個實例。 – 2012-03-14 00:06:40

+0

@ G.Bach是的,這就是'overriding'的全部要點,用於根據動態類型確定要調用的方法。 – 2012-03-14 00:10:39

+0

那麼我是否正確理解這一點,即重載方法名稱綁定的方法是在編譯時決定的,而哪個類的重寫(然後已經選擇)方法的問題是在運行時決定的?我所嘗試的是:如果添加一個方法Af(int),調用z。( - 6)將以Bf(int)結尾,那麼對於那個調用似乎發生的事情是,在編譯時,編譯器選擇Af(int),因爲z在類型A的變量中,而在運行時,這會被Bf(int)覆蓋。我沒有意識到這一點。 – 2012-03-14 00:24:22

3

靜態類型的zA這樣z.f(-6)可以僅結合在A的方法,在這種情況下是A.f(int)

該語言的設計,這種方式使

A z = new B(3.0); 
z.f(-6); 

總會表現得一樣

A z = complicatedWayToComputeTrue() ? new B(3.0) : new A(3.0); 
z.f(-6); 

如果編譯器綁定到一個不同的方法簽名,因爲它可以證明A z總是持有一個B然後這會引起各種非本地影響的語言,使它真的很難調試或維護Java程序。

想象有人試圖維持

final A z = complicatedWayToComputeTrue() ? new B(3.0) : new A(3.0); 
// 1000 lines elided 
z.f(-6); 

通過改變它

A z = new B(3.0); 
// 1000 lines elided 
z.f(-6); 

如果編譯器現在可以證明A始終是一個B並結合Z.f的方法B,維護者會感到困惑。

+0

這個答案的第一句話(和主要內容)是**完全不正確**。當然,當它的類型被超類引用時,你可以在子類上調用一個方法 - 這是OOP的本質! – Bohemian 2012-03-13 23:31:24

+0

@波希米亞,試試這個程序。 (class A {} class B extends A {void f(){} public static void main(String [] args){A x = new B(); A.foo(); }}。你會得到一個編譯錯誤:「找不到符號方法foo()」。 – 2012-03-13 23:34:40

+0

你們都是對的,視情況而定。如果重寫子類中的方法,那麼即使在引用父類的實例時,如果重載或創建新方法,它顯然也會被綁定,而不是太多。 – 2012-03-13 23:38:19

1

Java是單一調度,而您試圖執行的操作是double dispatch(調用的方法取決於動態運行時類和參數)。

在Java中調用方法的簽名是在編譯時確定的;這意味着對象的聲明類決定了綁定的方法。覆蓋子類中的方法會影響綁定的實現,但重載方法不會(因爲重載的方法具有不同的簽名)。

在類B中,當使用一個聲明爲A類的對象時,使用接受int的版本重載f(),但此方法看起來並不存在(您不能調用它,也不會調用)。

總結:

  • 編譯器結合在編譯時間的方法簽名。
  • 此綁定簽名取決於對象和參數的聲明(編譯時)類型。
  • 如果在子類中重寫方法,則在調用子類時(不管聲明的類型如何),將選擇重寫的方法。
  • 重載不是重寫,重寫你需要相同的方法簽名(以及除協方差外)。
0

嗯,我可能是錯的,但如果你A類型的對象z鏈接到B類型的對象,它仍然會綁定爲B類型,這就是爲什麼它的執行方法B類,而不是在A類。請注意,由於您沒有使用new,因此您並未創建A類型的對象。

而且正如Mike Samuel所說,默認-6應該被認爲是int,但這不符合您的解釋。我會盡力找出適當的答案。

+0

不是「默認」。在Java中,'-6'總是**一個'int'文字。 Java沒有像[Go](http://golang.org/doc/go_spec.html#Constants)這樣的任意精度常量,其中「數字常數表示任意精度的值並且不會溢出」。 – 2012-03-13 23:46:53