2013-11-25 75 views
1

问题:在我的继承类的字段初始化完成之前调用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

所以我觉得我的步骤是:

  1. 我打电话的ExtendedGeometry(隐含的)构造函数。
  2. 我隐式地初始化一个私有成员变量。
  3. 超级构造函数调用我的overriden方法,调用loadModel()
  4. - >奇怪:成员变量现在为空,但声明!
  5. 我再次初始化成员变量。
  6. 我用值填充它。
  7. 在主循环中,我打印变量的内容,但它是空的。

因此,在第4步,我预计该变量不为空,并在步骤7中填充。

为了解决它,我发现了三个解决方案:

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

所以尝试了所有这些东西后,我意识到的解释似乎是,我的超能与声明但不是初始化我的子类的工作。 这使我得出的结论是,完成的步骤是:

  1. 声明被提升。 (在编译期间)
  2. 我调用ExtendedGeometry的(隐式)构造函数。
  3. 调用构造函数super()
  4. 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 
     } 
    } 
} 

回答

0

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

您已经遇到了一个基本的OOD规则,您应该“从不”从构造函数中调用非静态的非最终方法。如果构造函数调用可以被子类覆盖的方法,那么这种类型的错误太常见了。

+0

感谢您的额外解释,我从来没有听说过这个基本规则。我经常使用它,因为我看到并使用了构造器+初始化器原则。很好知道! –