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

新手上路,请多包涵

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

即使是我能想到的最丑陋的 hack 也会被编译器拒绝:

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

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

原文由 fredoverflow 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 449
2 个回答

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

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

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

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

原文由 Keppil 发布,翻译遵循 CC BY-SA 3.0 许可协议

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

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

我从不建议使用这些技巧。

原文由 Serkan Arıkuşu 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题