2013-04-22 94 views
1

我瞭解在正常情況下,這樣的警告:構造函數調用可覆蓋方法(延遲調用)

class Test { 
    public Test() { 
    hello(); 
    } 
    public void hello() {} 
} 

但是如果我們有這樣的:

class Test { 
    public Test() { 
    // Put the call on a queue that will be executed later 
    queue.submit(new Runnable() { 
     public void run() { 
     hello(); 
     } 
    }); 
    } 
    public void hello() {} 
} 

,那裏電話爲hello()將不會立即發生。即使在子類準備就緒後回調執行很長的情況下,這仍然很糟糕/有風險嗎?

回答

2

這仍然是壞的/即使在回調執行長後的子類是準備的情況下冒險構建

是的,它仍然有風險。構造函數調用是而不是原子,因此如果在另一個線程中執行實例方法,則從構造函數中推遲實例方法的調用並不安全,因爲您無法保證在線程(最終)時該對象將被完全構造,調用。

現在,假設,如果子類對象被完全構造(強調如果),那麼,未來的回調將是安全的。換句話說,它不會繞過部分構建的對象,而這個對象在訪問它時非常危險。

+0

在我的問題,我的狀態「即使在回調執行長後的子類是準備建造案」雖然。例如,即使我這樣做:Test t = new Test(); queue.start()(想象start()在這裏啓動處理線程)。所以基本上我想知道在我完全確定子類已經完成構建的情況下它是否還存在風險? (或者即使第二個線程在構造對象之後啓動也不能保證) – Zitrax 2013-04-22 12:20:40

+0

@Zitrax - 但是你正在做出的假設不能在你的代碼中強制執行,也就是說其他地方的某些約束會減慢任務執行的速度直到你不會遇到問題。您也正在假設隊列的內部實現(也就是說,它不會嘗試以不安全的方式訪問提供的任務)。這就是太多的假設imo,它更安全地完全構建對象,然後調用另一個方法,這將導致其任務被排入隊列。 – Perception 2013-04-22 12:26:47

+0

我的例子並不好,因爲它涉及到假設,但我的問題是假設子類對象在調用回調時被完全構造 - 是否安全?換句話說,「是完全構造的對象」是從構造函數調用重寫方法的唯一問題,還是其他方面呢? – Zitrax 2013-04-22 13:49:20

1

我會說是的,這是有風險的只是因爲你是在Test對象暴露給第二個線程之前,它是完全構建。

如果您需要通過確保調用hello來控制Test實例的初始化,請考慮使用工廠方法進行實例化。再加上私有構造,你可以保證hello安全地呼籲所有Test對象:

public Test { 
    /** 
    * Factory method to create Test instances and safely call public method 
    */ 
    public static Test getInstance() { 
     Test test = new Test(); 
     test.hello(); 
     return test; 
    } 

    /** 
    * Private constructor to control Test object creation. 
    */ 
    private Test() { 
     super(); 
    } 

    public void hello() { 
    } 
}