2010-04-21 74 views
4

我可以做以下事情嗎?我可以在其構造函數中引用一個對象嗎?

public Manager(String userName) { 
    game = new Game(userName); 
    game.addManager(this); 
} 

的問題是,我指的是在其構造方法的對象(this)(它實際上是創建前)。

+1

技術上的對象已經創建* *在這一點上(否則你不能訪問它的屬性之一),但它並不完全正確初始化* *呢。 – 2010-04-21 14:28:35

回答

2

是的完美法律在Java中,但不建議。有關this關鍵字的更多詳細信息,請參閱here

+1

雖然這種技術在Java中有效,但它非常危險。 – 2010-04-21 14:27:12

+2

完美有效??它只是編譯而已,僅此而已。 – Roman 2010-04-21 14:27:26

+2

我認爲這個答案會更好,如果它說「它是完全合法的Java」。但就像在生活中一樣,僅僅因爲它是合法的並不能成爲一個好主意。 – Yishai 2010-04-21 14:29:27

3

是的,你可以做到這一點,但你不應該這樣做

問題是,當構造函數仍在運行時發佈this可能會產生各種奇怪的副作用,因爲在構造函數仍在運行時某些常見保證不成立(例如final變量看起來可能會更改他們的價值,而構造函數仍然運行)。

This IBM developerWorks article描述了構建對象時要採取的預防措施以及這些預防措施背後的推理。雖然本文根據多線程討論了該主題,但在構建期間未知/不可信代碼獲取對this的引用時,單線程環境中可能會出現類似問題。

(最後一段從one of my earlier answers「被盜」)。

1

正如@詹姆斯所說,你可以,但它不一定是你想要做的事情。如果game.addManager嘗試訪問Manager的某些屬性,則最終可能會嘗試訪問尚未初始化的Manager的屬性。更好的方法是讓外部對象調用某個init方法(或某種生命週期方法)來添加管理器,而不是在構造函數中執行。

0

這種技術違反了Java併發概念之一 - 安全的出版物。您應該使用init()方法用於此目的或其他技術。

你看,你可以在這個引用已經被轉義後,在你的構造函數中初始化一些final字段(或者做一些其他初始化)。如果您在構造函數中將對象的實例傳遞給另一個對象,則可以在構建過程中獲得回調。它會導致不一致的行爲,NPE,死鎖等等。

4

雖然它是合法的Java,並且在你描述的情況下(它是構造函數的最後一行),這是一件相當安全的事情(在某些邊緣情況下可以免除),作爲一種習慣要做的事情,並且喜歡使用goto(在支持關鍵字的語言中),它應該是你認爲漫長而艱難的事情。對於你的情況下,更好的做法是使構造私有,取出調用addManager和揭露靜態工廠方法:

public static Manager createManager(String userName) { 
     Manager manager = new Manager(userName); 
     manager.game.addManager(manager); 
     return manager; 
} 

我還要指出的是階級之間那種相互依存的(經理知道關於遊戲和遊戲知道經理)絕對是一種代碼味道,我會關心這個需求,就像我將要從構造函數中傳遞它一樣。

+1

+1爲相互依賴性代碼異味 – sleske 2010-04-21 14:39:29

0

男孩,這是不安全的!雖然有效的代碼,但不是很好的設計!您的代碼允許「this」引用在對象正確構造之前轉義。

想象一下,game.addManager()會調用「this」引用上的某個方法xxx()。 而且我們有一個Manager的子類ChildManager,它覆蓋了方法xxx(),並且此方法依賴於ChildManager中的一個字段(超級構造函數到達其最後一行代碼時未初始化)。 game.addManager()會在ChildManager中看到未初始化的字段值,這非常非常危險!

示例代碼:

public class Manager { 
    Game game; 
    public Manager (String userName){ 
     game = new Game(userName); 
     game.addManager(this); 
    } 
    public void xxx(){ 

    } 
} 

public class ChildManager extends Manager { 
    String name; 
    public ChildManager (String username){ 
     super(username); 
     name = username; 
    } 

    public void xxx(){ 
     System.out.println(name); 
    } 
} 

public class Game { 
    public Game (String userName){ 

    } 

    public void addManager (Manager m){ 
     m.xxx(); 
    } 
} 
相關問題