2013-07-06 62 views
2

我去'刷新'我的Java,只是意識到,顯然我不懂基本概念!這裏有一個簡單的我想不通:Java抽象類+繼承 - 範圍或名稱解析

public abstract class Robot { 

    private String model = "NONAME"; 

    public Robot() { 
     System.out.println("Making a generic " + model + " robot, type: " + this.getClass()); 
    } 

    public String getModel() { 
     return model; 
    } 
} 

OK,和子類:

public class Terminator extends Robot { 
    private String model; 

    public Terminator(String model) { 
     super(); 
     System.out.println("Making a " + model + " terminator, type: " + this.getClass()); 
     this.model = model; 
    } 
} 

然後我運行一個簡單的例子,希望「T1000」要打印:

Robot r1 = new Terminator("T1000"); 
    System.out.println(r1.getModel()); 

沒有骰子!打印「NONAME」。在此之前,我從這個構造函數輸出:

  • 製作一個通用的NONAME機器人類型:類com.akarpov.tutorial.Terminator
  • 製作T1000終止,類型:類com.akarpov.tutorial.Terminator

因此,我看到Java發現我的類的運行時實例是Terminator,這是被告知要做什麼的新事實。顯然,終結者實例確實保留了模型==「T1000」的副本。但在調試器(IntelliJ)中檢查r1對象我在不同的地址(顯然)使用不同的字符串看到名爲'model'的變量兩個。顯然,如輸出所示,抽象類中的getModel將拾取Robot類中定義的默認值,而不是傳遞給Terminator構造函數(並保留在對象內)的默認值。

這是什麼我不明白抽象類和繼承,以及我將如何去有一個默認值和默認行爲(即getModel),其中拿起一個特定的數據(即「T1000」)在一個子類? 謝謝!如果這個問題已經被解決了很多次,我很抱歉 - 我看了看,但沒有看到我跳過。

回答

1

你遇到的問題是,你確實創造了兩個變量。現在,使用您的代碼,當您撥打r1.getModel()時,您將獲得原始基類model

如果您希望能夠從子類設置模型,您有幾個選項。你可以通過聲明一個新的String model來改變開始的方向,但是你必須繼續重寫超類中的getModel()方法,這樣你的子類將會看到它自己的model而不是超類model

public class Terminator extends Robot { 
    private String model; 

    public Terminator(String model) { 
     super(); 
     System.out.println("Making a " + model + " terminator, type: " + this.getClass()); 
     this.model = model; 
    } 

    @Override 
    public String getModel(){ 
    return model; 
    } 
} 

另一種選擇是在超類中爲model創建一個公共受保護的setter方法。

public abstract class Robot{ 

    private String model = "NONAME"; 

    public Robot() { 
     System.out.println("Making a generic " + model + " robot, type: " + this.getClass()); 
    } 

    public String getModel() { 
     return model; 
    } 

    protected void setModel(String str){ 
     this.model = str; 
    } 
} 

然後你只想有你的終結者對象調用setModel(model)getModel()之前,你將有所需的輸出。

+0

你說得對,當然 - 這是我在發佈2分鐘後意識到的。不過,我已經接受了答案,否則我會選擇你的! – alexakarpov

2

您的問題是與私人修改器...模型變量在兩個類中分別存在兩次。私人手段只對那個班級可見。您可能想要使用setter方法。

+0

我在發帖2分鐘後發現,當我去和我的配偶談論這個問題時 - 問題不是私人的(我嘗試保護),而是因爲我*將機器人模型藏在另一個模型在終結者!從終結者中移除該變量解決了這個問題。 – alexakarpov

+0

......開始思考這個問題,你比我第一個想法更正確 - 因爲我確實忽視了這樣一個事實,即私生子不是遺傳的。 – alexakarpov

1

哦天哪我知道了之後發帖。 我的錯誤是在Terminator中聲明另一個String模型,導致在Robot中隱藏模型 - 因此是兩個副本。刪除它解決了這個問題。哎呀!

+0

只是刪除變量聲明不能解決問題。您還需要向基類添加一個字符串構造函數,並從子類中委託給它,並在子類構造函數中刪除'this.model = model'。 –

+0

我認爲你在一般情況下是正確的 - 但在我的情況下,默認情況下,基類Robot將模型字段初始化爲「NONAME」。因此,如果我的理解是正確的,那麼從終結者中移除模型字段使得超類模型成爲唯一 - 並且這是解決的子類中的一個「模型」。 – alexakarpov

+1

私有成員不會被繼承,所以只有當您碰巧在'Robot'內聲明'Terminator'爲一個靜態嵌套類時纔會工作。或者,當然,如果您在此期間提高了'model'的訪問級別。 –

0

讓我們通過下面這行代碼

Robot r1 = new Terminator("T1000"); 

所以這就要求終結者(String)構造。構造函數的第一件事是明確地調用超類的構造函數。它會自動完成,但你已經明確地寫了super(),沒關係。父類的構造做1兩件事:

System.out.println("Making a generic " + model + " robot, type: " + this.getClass()); 

好了,所以它打印出「讓通用NONAME機器人類型:終結者」,因爲這是該方法所看到的。它沒有對任何「模型」變量的本地引用,因此它使用Robot類中定義的實例變量。然後控制返還給終結者構造函數,它進行打印出來

System.out.println("Making a " + model + " terminator, type: " + this.getClass()); 

但是這一次它的工作原理,你如何期望,因爲模型變量是由類調用的方法傳遞,因此它陰影的實例變量。因此它的值是「T1000」。希望這是有道理的

+0

是的,你是對的。這解釋了爲什麼在Terminator構造函數中我看到「T1000」;但我的問題是讓抽象類具有通用的「getModel」行爲以某種方式與子類「共享狀態」 - 由於我已將「T1000」分配給子類實例變量,因此該子類無法工作,而超類方法只能看到它自己的 - 單獨的實例變量。不過,接受你指出本地vs實例的答案,我也錯過了! – alexakarpov