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

fre*_*low 36 java inheritance constructor initialization constructor-chaining

在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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Kep*_*pil 26

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

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

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

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


Ser*_*uşu 11

超级构造函数将在任何情况下运行,但由于我们正在谈论"最丑陋的黑客",我们可以利用这个

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();
    }
} 
Run Code Online (Sandbox Code Playgroud)

我从不建议使用这些黑客.


And*_*mas 8

正如其他人所说,您不能在调用超类构造函数之前初始化实例字段。

但是有解决方法。一种是创建一个工厂类来获取值并将其传递给派生类的构造函数。

class DerivedFactory {
    Derived makeDerived( String someParameter ) {
        int a = getValueFromDataBase();
        return new Derived( someParameter, a );
    }
}


class Derived extends Base
{
    private final int a;

    Derived(String someParameter, int a0 ) {
        super(hack(someParameter, a0));
        a = a0;
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)


小智 8

我有办法做到这一点.

class Derived extends Base
{
    private final int a;

    // make this method private
    private Derived(String someParameter,
                    int tmpVar /*add an addtional parameter*/) {
        // use it as a temprorary variable
        super(hack(someParameter, tmpVar = getValueFromDataBase()));
        // assign it to field a
        a = tmpVar;
    }

    // show user a clean constructor
    Derived(String someParameter)
    {   
        this(someParameter, 0)
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)