java - 继承类和父类(super class)的初始化顺序

标签 java inheritance initialization declaration

问题:我说的对吗 super()在继承类的字段初始化完成之前调用构造函数,这样初始化就可以覆盖我已经初始化的内容?

标题和问题可能听起来有些令人困惑,所以我会尽力澄清我的问题。

我有一个abstract class Geometry其中有 protected abstract void方法。 该方法在 Geometry 的构造函数中调用。我发现这个电话导致了我遇到的问题。

在我的扩展类(class)中,ExtendedGeometry我通过调用私有(private)方法来实现抽象方法:

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

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

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

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

所以我认为我的步骤是:

  1. 我调用 ExtendedGeometry 的(隐式)构造函数.
  2. 我隐式初始化一个私有(private)成员变量。
  3. super 构造函数调用我的重写方法,该方法调用 loadModel() .
  4. -> 奇怪:成员变量现在为 null,但已声明!
  5. 我再次初始化成员变量。
  6. 我用值填充它。
  7. 在主循环期间,我打印了变量的内容,但它是空的。

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

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

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

因此,在尝试了所有这些内容之后,我意识到解释似乎是我的父类(super class)能够使用声明,但不能使用我的子类的初始化。 这使我得出结论,已完成的步骤是:

  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类,它的父类(super class):

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
        }
    }
}

最佳答案

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

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

关于java - 继承类和父类(super class)的初始化顺序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20189065/

相关文章:

java - 如何使用密码来解码字符串?

java - 使用 Spring @Value 注解时禁用 JndiProperty 源

java - 在antlr访问者模式中如何从一种方法导航到另一种方法

java - JPA 继承

C# Winforms 用对象继承用户控件并修改

initialization - 初始化结构值和字符串键的关联数组

java - 哪个 jar 包含 com.azure.core.credential.TokenCredential 以及在哪里获取它?

c++ - 在从 Boost 派生的类派生的类中自动禁止复制是不可复制的吗?

C++11 普通旧对象默认值

c - C中的三元搜索树,指向结构体指针的问题