问题:在我的继承类的字段初始化完成之前调用super()
构造函数并且由此初始化可以覆盖我已经初始化的内容吗?使用继承类和超类初始化的顺序
标题,也许问题听起来有些混乱,所以我会尽力澄清我的问题。
我有一个abstract class Geometry
它有一个protected abstract void
方法。 在Geometry的构造函数中调用此方法。我发现这个电话造成了我的问题。
在我的扩展类,ExtendedGeometry
我通过调用私有方法实现的抽象方法:
@Override
protected void createGeometry() {
loadModel();
}
的private void loadModel()
方法FILLES一个vertexList
和完成。
vertexList
定义为private ArrayList<Integer> vertexList = new ArrayList<Integer>();
。
当我在我的模型上调用update()
时,vertexList看起来是空的。甚至在此之前,尽管我如上所示直接初始化变量,但在loadModel()
方法中似乎是null
。
所以我觉得我的步骤是:
- 我打电话的
ExtendedGeometry
(隐含的)构造函数。 - 我隐式地初始化一个私有成员变量。
- 超级构造函数调用我的overriden方法,调用
loadModel()
。 - - >奇怪:成员变量现在为空,但声明!
- 我再次初始化成员变量。
- 我用值填充它。
- 在主循环中,我打印变量的内容,但它是空的。
因此,在第4步,我预计该变量不为空,并在步骤7中填充。
为了解决它,我发现了三个解决方案:
- 第一种方案是在我
ExtendedGeometry
的构造函数调用loadModel()
而不是从重写createGeometry()
调用它。 - 第二种解决方案是简单地为
Geometry
类引入init()
方法,然后调用createGeometry()
。 - 第三种解决方案是只声明成员变量,但不初始化它。
所以尝试了所有这些东西后,我意识到的解释似乎是,我的超能与声明但不是初始化我的子类的工作。 这使我得出的结论是,完成的步骤是:
- 声明被提升。 (在编译期间)
- 我调用
ExtendedGeometry
的(隐式)构造函数。 - 调用构造函数
super()
。 ExtendedGeometry
的成员被初始化。 (什么会无视super()
进行初始化。)
所以我的问题是一个简单的是/否的问题(这可能与解释延长,如果我错了 - 或补充,如果我是正确的):
我说的super()
构造函数在我的继承类的字段初始化完成之前被调用,通过这个初始化可以覆盖我已经初始化的东西吗?
我首先想到这是一个可见性问题,但在追踪问题时,这似乎是我能想到的唯一解决方案。
只是为了完整,也许在这里更好地理解我的代码减少了95%,抱歉它没有JavaDoc。但我添加了一些评论来显示问题并解释。
最重要的一个,这里是ExtendedGeometry.java
: 封装几何;
import java.util.ArrayList;
public class ExtendedGeometry extends Geometry {
// PROBLEM 1: the ArrayList is already initialized here
private ArrayList<Integer> vertexList = new ArrayList<Integer>();
// usually this is Vector3f from lwjgl, but for
// STO I changed it to Integer
private void loadModel() {
// PROBLEM 2: but the ArrayList is null here!
System.out.println("Initializing vertexList which is " + vertexList);
// PROBLEM 3: leaving the following lines out
// does not change anything in the output
// of the update() method
if(vertexList == null)
vertexList = new ArrayList<Integer>();
// PROBLEM 4: filling the "newly" initialized vertexList,
// but not the old one
vertexList.add(1);
vertexList.add(2);
vertexList.add(3);
}
public void update() {
// PROBLEM 5: as you can see, you see nothing:
// the vertexList has no content
System.out.println("vertexList of size: " + vertexList.size());
for(Integer i : vertexList) {
System.out.print(i);
}
System.out.println();
}
/*// PROBLEM 6/SOLUTION: If I leave out the loadModel in
// createGeometry() but use this constructor instead
// it works
public SpecializedGeometry() {
loadModel();
}
*/
@Override
protected void createGeometry() {
loadModel();
}
}
的Geometry.java
类,其超类:
package geometry;
public abstract class Geometry {
public Geometry() {
// every geometry has to be created
createGeometry();
}
/*// If I add an init method like this, it works
public void init() {
createGeometry();
// of course this line has to be removed
// from the constructor
}
*/
// this is the abstract method to create a geometry
protected abstract void createGeometry();
}
的Simulation.java
只是表明我的程序是如何打造一般:这里
package simulation;
import geometry.ExtendedGeometry;
public class Simulation {
private ExtendedGeometry geometry = null;
public Simulation() {
}
public boolean init() {
// initializes all simulation stuff,
// now only creates the geometry
geometry = new ExtendedGeometry();
// geometry.init(); // this is a possible fix, see Geometry.java
return true;
}
public void update() {
// does calculations and updates everything
// accordingly
if(geometry != null) {
geometry.update();
}
}
}
的MainWindow.java
,没有什么太特别的:
package main;
import simulation.Simulation;
public class MainWindow {
private Simulation simulation = null;
public boolean init() {
// create the simulation and initialize it
// usually here is a bunch of initializations
simulation = new Simulation();
return simulation.init();
}
public void run() {
// in this example I close after 10 loop runs,
// of course 1 would be sufficient as well
int x = 0;
// the main loop handles inputs, event manager,
// updates, drawing, ...
while(x++ < 10) {
// update simulation
simulation.update();
}
}
}
最后无聊Main.java
:
package main;
public class Main {
public static void main(String[] args) {
MainWindow mw = new MainWindow();
if(mw.init()) { // init and
mw.run(); // run main loop
}
}
}
感谢您的额外解释,我从来没有听说过这个基本规则。我经常使用它,因为我看到并使用了构造器+初始化器原则。很好知道! –