2013-11-25 77 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

感謝您的額外解釋,我從來沒有聽說過這個基本規則。我經常使用它,因爲我看到並使用了構造器+初始化器原則。很好知道! –