超级构造函数运行之前初始化场?(Initialize field before super cons

2019-08-18 02:47发布

在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;
    }
}

注意:这个问题消失了,当我从继承切换到代表团,但我还是想知道。

Answer 1:

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

按照语言规范 ,实例变量,甚至没有初始化,直到super()已经调用。

这些都是在课堂实例的创建,从环节采取的构造步执行的步骤:

  1. 分配参数的构造函数来创建新的参数变量此构造函数调用。
  2. 如果此构造在同一类(使用本)另一个构造的一个明确的构造函数调用(§8.8.7.1)开始,然后评估该构造函数调用递归使用这些相同的五个步骤的参数和过程。 如果构造函数调用完成突然,那么这个过程完成突然出于同样的原因; 否则,继续执行步骤5。
  3. 此构造不与其他构造的一个显式构造函数调用开始在同一个类(使用此)。 如果此构造比其他对象的一类,那么这个构造将与(使用超)超类的构造函数的显式或隐式调用开始。 评估认为超类构造函数调用递归使用这些相同的五个步骤的参数和过程。 如果构造函数调用完成突然,那么这个过程完成突然出于同样的原因。 否则,继续步骤4。
  4. 执行实例初始化和实例变量初始值设定为这样的类,实例指派变量初始化的值,以相应的实例变量,在它们出现文本形式在类源代码中的左到右的顺序。 如果任何一个异常这些初始化的结果执行,则没有进一步的初始化处理和此过程与相同的异常突然结束。 否则,继续第5步。
  5. 执行此构造体的其余部分。 如果突然执行完成,那么这个过程完成突然出于同样的原因。 否则,这个过程正常完成。


Answer 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();
    }
} 

我从来不建议使用这些类型的黑客。



Answer 3:

我有一个办法做到这一点。

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)
    }

    ...
}


Answer 4:

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

但也有变通方法。 一个是创建一个工厂类,获取值,并将其传递给派生类的构造函数。

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;
    }
    ...
}


Answer 5:

它禁止的Java语言规范(第8.8.7) :

一个构造体的第一个语句可以是相同的类或直接超类的另一个构造的明确调用。

构造函数体应该是这样的:

ConstructorBody:

 { ExplicitConstructorInvocationopt BlockStatementsopt } 


文章来源: Initialize field before super constructor runs?