继承和超类的初始化顺序

时间:2013-11-25 09:54:51

标签: java inheritance initialization declaration

问题:在我继承的类的字段初始化完成之前调用super()构造函数是否正确?初始化可以覆盖我已经初始化的内容吗?

标题和问题听起来有些令人困惑,所以我会试着澄清我的问题。

我有一个abstract class Geometry,其protected abstract void方法。 在Geometry的构造函数中调用此方法。我发现这次电话会引起我的问​​题。

在我的扩展类ExtendedGeometry中,我通过调用私有方法实现了抽象方法:

@Override
protected void createGeometry() {
    loadModel();
}

private void loadModel()方法填充vertexList并完成。

vertexList定义为private ArrayList<Integer> vertexList = new ArrayList<Integer>();

当我现在在我的模型上调用update()时,vertexList似乎是空的。甚至在此之前,虽然我如上所示直接初始化变量,但在null方法中似乎是loadModel()

所以我认为我的步骤是:

  1. 我调用ExtendedGeometry。(/ li>)的(隐式)构造函数
  2. 我隐式初始化一个私有成员变量。
  3. 超级构造函数调用我的override方法,该方法调用loadModel()
  4. - &GT;奇数:成员变量现在为null,但声明了!
  5. 我再次初始化成员变量。
  6. 我用值填充它。
  7. 在主循环期间,我打印变量的内容,但它是空的。
  8. 因此,在步骤4中,我希望变量不为空,并在步骤7中填充。

    为了解决这个问题,我找到了三个解决方案:

    • 第一个解决方案是在loadModel()的构造函数中调用ExtendedGeometry,而不是从覆盖createGeometry()调用它。
    • 第二种解决方案只是为init()类引入Geometry方法,然后调用createGeometry()
    • 第三种解决方案是简单地只声明成员变量,而不是初始化它。

    所以在尝试了所有这些东西后,我意识到解释似乎是我的超类能够使用声明而不是我的子类的初始化。 这使我得出以下结论:

    1. 声明得到提升。 (在编译期间)
    2. 我调用ExtendedGeometry。(/ li>)的(隐式)构造函数
    3. 调用super()构造函数。
    4. ExtendedGeometry的成员已初始化。 (什么覆盖了使用super()完成的初始化。)
    5. 所以我的问题是一个简单的是/否问题(可以通过解释扩展,如果我错了 - 或者如果我是对的话还可以补充):

      我是否正确在我的继承类的字段初始化完成之前调用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
              }
          }
      }
      

1 个答案:

答案 0 :(得分:0)

我相信你已经回答了你自己的问题,是的,扩展类在初始化子类之前已经完全初始化了。

你已经遇到了OOD的基本规则,你应该“永远”从构造函数中调用非静态的非final方法。如果构造函数调用可被子类覆盖的方法,则此类型的错误太常见了。