2013-03-10 78 views
11

假設我有一個實現了Runnable接口的抽象Base類。可以從Java的構造函數中調用抽象方法嗎?

public abstract class Base implements Runnable { 

    protected int param; 

    public Base(final int param) { 
     System.out.println("Base constructor"); 
     this.param = param; 
     // I'm using this param here 
     new Thread(this).start(); 
     System.out.println("Derivative thread created with param " + param); 
    } 

    @Override 
    abstract public void run(); 
} 

這裏是少數派生類之一。

public class Derivative extends Base { 

    public Derivative(final int param) { 
     super(param); 
    } 

    @Override 
    public void run() { 
     System.out.println("Derivative is running with param " + param); 
    } 

    public static void main(String[] args) { 
     Derivative thread = new Derivative(1); 
    } 

} 

重點是我希望我的基類做一些普通的東西,而不是每次都複製它。 其實,這是運行良好,輸出始終是相同的:

基本構造 與參數1 衍生物,其與參數1

運行,但它是安全用Java創建衍生線程啓動一個線程調用抽象方法在構造函數中?因爲在C++和C#中,在大多數情況下它是不安全的,據我所知。 謝謝!

回答

21

此代碼演示了爲什麼你應該從未調用一個抽象方法,或任何其他重寫方法,從構造函數:

abstract class Super { 
    Super() { 
     doSubStuff(); 
    } 
    abstract void doSubStuff(); 
} 

class Sub extends Super { 
    String s = "Hello world"; 

    void doSubStuff() { 
     System.out.println(s); 
    } 
} 

public static void main(String[] args) { 
    new Sub(); 
} 

運行時,該打印null。這意味着構造函數中唯一的「安全」方法是私有的和/或最終的。

另一方面,您的代碼實際上並沒有從構造函數中調用抽象方法。相反,您將未初始化的對象傳遞給另一個線程進行處理,這更糟糕,因爲您啓動的線程可能會優先執行並在您的初始化完成之前執行。

+1

如果有構造函數調用抽象或虛方法*,它的約定指定它可以從構造函數中調用,從這樣的上下文調用時必須是安全的,並且只能調用其他類似的方法安全*? – supercat 2013-11-13 22:39:42

+1

@supercat:我想這取決於你對文檔的信任程度,你相信其他人遵守這些文檔的程度,以及你相信每個可能擴展這樣一個類或其任何子類以適當地傳播或記住請參閱此類警告。在我看來,這是相當多的信任。我更喜歡可以通過自動化測試證明的事情,而不是。 – 2013-11-13 23:08:18

+2

夠公平的。我想到的最大的用例是一個派生類可能會覆蓋一個方法來返回一個常量,這個常量對於每個子類的所有實例必須是相同的,並且在構造函數和其他地方需要這個常量,所以一個典型的實現是'int getWoozleForType(){return 23;}'。將這樣的東西傳遞給構造函數,並將其存儲在實例字段中似乎有點兒瑣碎,我想不出任何其他方法。 – supercat 2013-11-13 23:20:33

3

從構造函數調用抽象方法是一種非常糟糕的做法。從構造函數調用的方法應始終是私有的或最終的,以防止重寫。

請參閱此鏈接到一個問題here

2

不是一個好主意,因爲運行時()被調用,衍生對象可能沒有被初始化。如果run()取決於Derivative中的任何狀態,它可能會失敗。

在你的簡單情況下,它的工作原理。但是,這個子類沒有意義。你可以簡單地

public Base(final int param, Runnable action) { 

    new Thread(action).start(); 
1

傳遞this出的構造被稱爲「從構造讓this逃逸」,並可能導致一些特別惡劣和莫名其妙的錯誤,因爲對象可能是不一致的狀態。

this傳遞給另一個線程時,這種情況尤其如此,如本例所示。由於JVM有權對線程中的語句進行重新排序,因此可能會發生未定義的行爲/狀態。

相關問題