Java 在超级构造函数运行之前初始化字段?
在Java中,有没有办法在超级构造函数运行之前初始化字段 即使是我能想到的最丑陋的黑客也会被编译器拒绝:Java 在超级构造函数运行之前初始化字段?,java,inheritance,constructor,initialization,constructor-chaining,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) { s
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;
}
}
注意:当我从继承切换到委派时,问题消失了,但我仍然想知道。不,没有办法做到这一点 根据,在调用
super()
之前,实例变量甚至不会初始化
以下是在创建类实例的构造函数步骤中执行的步骤,这些步骤取自链接:
{ ExplicitConstructorInvocationopt BlockStatementsopt }
正如其他人所说,在调用超类构造函数之前,不能初始化实例字段 但也有解决办法。一种是创建一个工厂类,该类获取值并将其传递给派生类的构造函数
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;
}
...
}
超级构造函数在任何情况下都会运行,但由于我们讨论的是“最丑陋的黑客”,我们可以利用这一点
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();
}
}
我从不建议使用这种黑客手段。我有办法做到这一点
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)
}
...
}
虽然不可能直接执行,但可以尝试使用嵌套对象执行 以你的例子来说:
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定义为最后一个变量您是否试图预初始化字段
a
?我认为您无法做到这一点。在类中执行的任何初始化(即使它在构造函数之外)都会在调用super
后移动到每个构造函数。因此,超级构造函数总是在字段初始化之前运行。@fredsoverflow既然a
只能在Derived
中访问,那么在调用super()
之前初始化它又有什么关系呢?在您提供的示例中,立即初始化它并没有什么不同(除非您从基本构造函数调用重写的方法,这开始让人觉得很可疑)如果重写方法依赖于子类构造函数执行的任何初始化,那么该方法将不会像预期的那样运行。“丑陋的黑客:直接用java字节码创建派生类,如下面的回答:+1-你是对的。哎呀。我在思考C++。尽管如此——我认为没有必要进行黑客攻击——请看我的答案。正如OP所问,这并不是在超级构造函数运行之前。可能是你能得到的最接近的了。@Keppil我在回答“超级构造函数在任何情况下都会运行”中提到过,这只是在派生类中初始化变量的一种技巧。在我看来,这是最好的解决方案。我唯一要改变的是,我将在派生的类中放置一个静态工厂方法。@mkilic:非常确定