在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;
}
}
注意:当我从继承切换到委托时,问题就消失了,但我仍然想知道。
答案 0 :(得分:26)
不,没有办法做到这一点。
根据language specs,在进行super()
调用之前,实例变量甚至都没有初始化。
这些是在创建类实例的构造函数步骤期间执行的步骤,取自链接:
- 将构造函数的参数分配给新创建的参数 此构造函数调用的变量。
- 如果此构造函数以显式构造函数调用开头 (§8.8.7.1)同一类中的另一个构造函数(使用此), 然后评估参数并处理构造函数调用 递归地使用这五个相同的步骤。如果那个构造函数 调用突然完成,然后此过程完成 突然出于同样的原因;否则,继续执行第5步。
- 此构造函数不以显式构造函数开头 在同一个类中调用另一个构造函数(使用此方法)。如果 这个构造函数用于Object以外的类,然后是这个 构造函数将以a的显式或隐式调用开始 超类构造函数(使用超级)。评估参数和 使用递归进行超类构造函数调用的过程 这五个步骤相同。如果该构造函数调用完成 突然,这个程序突然完成同样的事情 原因。否则,请继续执行步骤4.
- 执行实例初始值设定项和实例变量初始值设定项 对于此类,分配实例变量的值 初始化器到相应的实例变量中 从左到右的顺序,它们在源中以文本形式出现 该类的代码。如果执行任何这些初始化程序 导致异常,然后不再处理初始化器 并且此过程突然以相同的异常完成。 否则,请继续执行步骤5.
- 执行此构造函数的其余部分。如果执行 突然完成,然后这个程序突然完成 同样的道理。否则,此过程正常完成。
醇>
答案 1 :(得分: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();
}
}
我从不建议使用这些黑客。
答案 2 :(得分:6)
我有办法做到这一点。
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)
}
...
}
答案 3 :(得分:4)
正如其他人所说,在调用超类构造函数之前,无法初始化实例字段。
但有一些解决方法。一种是创建一个获取值的工厂类,并将其传递给Derived类的构造函数。
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;
}
...
}
答案 4 :(得分:1)
Java language specification (section 8.8.7)禁止使用
构造函数体的第一个语句可能是显式的 调用同一个类或直接的另一个构造函数 超类。
构造函数体应如下所示:
ConstructorBody:
{ ExplicitConstructorInvocationopt BlockStatementsopt }
答案 5 :(得分:0)
虽然不可能直接做到,但是您可以尝试使用嵌套对象来实现
以您的示例为例:
open class Base {
constructor() {
Timber.e(this.toString())
}
}
class Derived {
val a = "new value"
val derivedObject : Base = object : Base() {
override fun toString(): String {
return "a has value " + a;
}
}
}
快乐的编码-可以破解,但可以工作:)记住将derivedObject定义为 LAST 变量