2014-04-18 51 views
0

我知道這是一個壞主意,它會導致錯誤。問題是,我需要「有意」的行爲。從構造函數調用抽象方法的安全替代

「低」:

// simplified example 
abstract class Low {   
    String name; 

    public Low(String name) { 
     this.name = name; 
    }  

    public Low(int id) { 
     this.name = getNameForId(id); 
    } 

    public Low() {} // will be loaded later 

    @Override 
    public String toString() 
    { 
     return name; 
    } 

    public void load(InputStream in) { 
     // --- grab ID from stream --- 
     this.name = getNameForId(id); 
    } 

    protected abstract String getNameForId(int id); 
} 

和 「高」:

class High extends Low {   
    public High(int id) { super(id); }  
    public High(String name) { super(name); } 
    public High() {} // will be loaded later 

    @Override 
    protected String getNameForId(int id) 
    { 
     return Registry.getName(id); 
    } 
} 

注意,在這種特殊情況下,它會工作得很好。但是一旦壓倒一切的方法需要使用某個領域,它就會崩潰。

如何更好地做到這一點?

+0

你能否舉一個事例分崩離析的例子?我不確定我是否遵守。 –

+0

如果類High返回getName()中的this.myNameField,它將不會被初始化並返回null。 – MightyPork

+0

是的,但Low構造函數會說'this.name = getName()',getName()會返回'this.name',所以這首先是無意義的。你會設置一個等於它自己的變量。 –

回答

1

你想分開加載低和高對象的ID的名稱。引入一個用於加載給定ID的名稱的接口。

public interface NameProvider 
{ 
    String getNameForId(String id); 
} 

爲每個名稱來源添加特定實現。

public class InputStreamNameProvider implements NameProvider 
{ 
    private InputStream inputStream; 

    // Constructor 

    public String getNameForId(String id) 
    { 
    // return name loaded via inputStream 
    } 
} 

public class RegistryNameProvider implements NameProvider 
{ 
    public String getNameForId(String id) 
    { 
    return Registry.getName(id); 
    } 
} 

你可以再添加一個新的構造爲低,是以NameProvider和字符串ID作爲參數

public Low(NameProvider provider, String id) 
{ 
    this(provider.getNameForId(id)); 
} 

或建造低或高的情況下,即使之前使用的名稱提供商。主要思想是分離從Low和High對象加載ID的名稱。

+0

使用@PostConstruct註解的抽象方法也會有效,因爲這樣可以確保當前類的super構造函數在隱式調用該方法之前完成初始化所有屬性。 – klaar

1

您可以避免通過添加public void load(int id)方法(如您對InputStream所做的操作)並刪除Constructor(int id)來調用抽象方法。

您可能需要添加一些工廠功能,以確保您的構造實例無法在沒有正確名稱值的情況下訪問。