2013-04-22 99 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() { 
    } 
}