2014-04-18 50 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)来调用抽象方法。

您可能需要添加一些工厂功能,以确保您的构造实例无法在没有正确名称值的情况下访问。