我的一个类继承自我使用的框架中的类。超类在其构造函数中调用一个方法,我在自己的类中覆盖它。 该方法使用我希望初始化的字段,超级构造函数调用它以避免NullPointerException。
有没有办法做到这一点?
以下是综合测试方案,我希望c
中的Child
在调用call
时不为空。
public class Test {
public static class Parent {
public Parent() {
super();
call();
}
// only called from parent constructor
public void call() {
System.out.println("Parent");
}
}
public static class Child extends Parent {
private Child c = this;
public Child() {
super();
}
// only called from parent constructor
public void call() {
System.out.println("Child, c is " + (c == null ? "null" : "this"));
}
}
public static void main(String[] args) {
new Child();
}
}
在Java 7之前,这是可能的。我可以通过这样的特技来解决这个问题:
public static class Child extends Parent {
private Child c;
private Child(Object unused) {
super();
}
public Child() {
this(c = this);
}
// only called from parent constructor
public void call() {
System.out.println("Child, c is " + (c == null ? "null" : "this"));
}
}
现在,这将不再适用。我很欣赏额外的安全性,但是来自super的电话会破坏它所获得的任何安全性并降低灵活性。
我想要一种规避这种限制的方法。 作为一种替代方案,我想知道通过限制来获得超级构造函数的情况。
答案 0 :(得分:3)
将在超类构造函数之前调用静态初始化程序。但是,您将无法设置任何非静态字段,因此很可能无法帮助。
http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html
非超级构造函数完成后调用非静态初始化块也没有帮助。
另一种方法可能是在从超级构造函数调用时不执行任何操作,并再次调用子构造函数,例如:
public Child() {
super();
call();
}
public void call() {
if (c==null) {
return;
}
System.out.println("do something with c now");
}
如果在超级构造函数中发生了更多依赖于此方法的东西,这将无效。
我必须同意EJP的观点,这是一个坏主意;找到一个完全不同的解决方案,不涉及折磨构造函数会好得多。
答案 1 :(得分:2)
请注意,您的类Child
由Java编译器转换为以下等效项:
public static class Child extends Parent {
private Child c;
public Child() {
super();
c = this;
}
// Remaining implementation
}
对于Java 6和7,这是相同的,当使用任何两个版本进行编译时,构造函数的生成字节代码甚至相同。在调用超级构造函数之后,始终会实例化本地字段。您使用什么编译器来“解决”工作?
这种限制非常基本。这样,您可以依赖首先应用的超级构造函数。想象一下,您的子构造函数正在使用此类中声明的final
字段。如果您不保证此构造函数执行顺序,则无法保证此字段已初始化。这种限制使Java更可靠。
答案 2 :(得分:2)
这是对“我想知道通过超级构造函数案例的限制所获得的内容”的答案。问题的一部分。
在构造过程中,X类中声明的字段可能处于三种状态:所有默认值,全部初始化为一致的工作值,以及其他任何内容。
目标似乎是X以外的类中的代码只能看到前两个状态中的一个。当任何X超类的非静态初始化程序或构造函数代码正在运行时,X的字段都处于默认状态。当X的任何子类的非静态初始化程序或构造函数代码正在运行时,所有X的字段都已初始化为完全一致的可用状态。
只有X初始化程序和构造函数代码必须处理处于不一致状态的X字段,一些是初始化的,一些是默认的,一些是部分初始化的。
通过从X超类初始化器或构造函数调用X方法可以避免这种情况,但这通常被视为反模式。问题是运行的X代码不是从部分构造的X中的初始化程序或构造函数本地调用的。如果该代码更改字段,则在X初始化程序和构造函数体运行时可能会覆盖此更改。
答案 3 :(得分:2)
这应该永远不会起作用。
请注意,在字节码级别,实际上允许这样做。在字节码中,您可以在调用超类构造函数之前设置当前类中声明的字段。但是,Java无法使用此行为。它仅由Java编译器秘密使用,以初始化为支持内部类而添加的合成字段。