在超级构造函数运行之前初始化字段?

在Java中,有没有办法在超级构造函数运行之前初始化字段?

即使是我能想到的最丑陋的黑客也被编译器拒绝了:

class Base
{
    Base(String someParameter)
    {
        System.out.println(this);
    }
}

class Derived extends Base
{
    private final int a;

    Derived(String someParameter)
    {
        super(hack(someParameter, a = getValueFromDataBase()));
    }

    private static String hack(String returnValue, int ignored)
    {
        return returnValue;
    }

    public String toString()
    {
        return "a has value " + a;
    }
}

注意:当我从继承切换到委派时,问题消失了,但我仍然想知道。


答案 1

不,没有办法做到这一点。

根据语言规范,在进行调用之前,实例变量甚至不会初始化。super()

以下是在类实例创建的构造函数步骤期间执行的步骤,取自链接:

  1. 将构造函数的参数分配给为此构造函数调用新创建的参数变量。
  2. 如果此构造函数以同一类中另一个构造函数的显式构造函数调用 (§8.8.7.1) 开头(使用此构造函数),则使用相同的五个步骤以递归方式评估参数并处理该构造函数调用。如果该构造函数调用突然完成,则此过程出于同样的原因突然完成;否则,请继续执行步骤 5。
  3. 此构造函数不以显式构造函数调用同一类中的另一个构造函数(使用此构造函数)开始。如果此构造函数用于 Object 以外的类,则此构造函数将从超类构造函数的显式或隐式调用(使用 super)开始。使用相同的五个步骤以递归方式评估超类构造函数调用的参数和过程。如果该构造函数调用突然完成,则此过程会出于同样的原因突然完成。否则,请继续执行步骤 4。
  4. 执行此类的实例初始值设定项和实例变量初始值设定项,将实例变量初始值设定项的值按从左到右的顺序分配给相应的实例变量,这些变量在类的源代码中以文本方式显示。如果执行这些初始值设定项中的任何一个导致异常,则不会处理进一步的初始值设定项,并且此过程会突然完成,并出现相同的异常。否则,请继续执行步骤 5。
  5. 执行此构造函数主体的其余部分。如果该执行突然完成,则此过程会出于同样的原因突然完成。否则,此过程将正常完成。

答案 2

超级构造函数在任何情况下都会运行,但是由于我们正在谈论“最丑陋的黑客”,我们可以利用这一点

public class Base {
    public Base() {
        init();
    }

    public Base(String s) {
    }

    public void init() {
    //this is the ugly part that will be overriden
    }
}

class Derived extends Base{

    @Override
    public void init(){
        a = getValueFromDataBase();
    }
} 

我从不建议使用这种黑客。