【问题标题】:Java: When I Instantiate a Subclass of an Abstract Class It Doesn't Recognize the Constructor of its SuperclassJava:当我实例化抽象类的子类时,它无法识别其超类的构造函数
【发布时间】:2011-05-02 20:32:50
【问题描述】:

我没有太多 Java 经验,但我看到代码中存在具有特定构造函数的抽象类,然后是没有构造函数的抽象类的子类。然后当子类被实例化时,它是用它的超类构造函数构造的。对吗?

我有这个抽象类:

public abstract class Tile{

    public int x;
    public int y;
    public int z;

    protected Color color;
    protected float friction;
    protected float bounce;
    protected boolean liquid;

    public void Tile(int x, int y, int z){
        this.x = x;
        this.y = y;
        this.z = z;
        init();
    }
    abstract protected void init();

还有这个子类:

public class TestTile extends Tile{
    protected void init(){
        color = Color.RED;
        friction = 0.1f;
        bounce = 0.2f;
        liquid = false;
    }
}

但是当我用这个实例化一个 TestTile 时:

Tile tile = new TestTile(0, 0, 0);

init() 方法永远不会运行。它里面定义的所有值都是空的。我尝试在子类中创建一个我认为可能是冗余的构造函数,它只是使用完全相同的参数调用 super,但是当我这样做时,即使 super(x, y, z) 是其中唯一的语句,它也会这样说:

TestTile.java:27:对 super 的调用必须是构造函数中的第一条语句

我想创建一堆 Tile 的子类来实现 Tile 的属性。如果这不是正确的方法,还有什么更好的方法?

如果与任何事情有关,我正在使用 32 位 Ubuntu Linux 11.04。

谢谢。

【问题讨论】:

标签: java class constructor subclass abstract


【解决方案1】:

你的构造函数不是属性构造函数格式,它是void,让它:

public Tile(int x, int y, int z){
        this.x = x;
        this.y = y;
        this.z = z;
        init();
    }

【讨论】:

    【解决方案2】:

    我没有看到带有三个参数的 TestTime 构造函数。我根本看不到任何 ctor,这意味着您所拥有的只是编译器为您提供的默认值。我是不是走得太快而错过了它?

    我建议您特别注意这一点。我会重新考虑这个设计:

    What's wrong with overridable method calls in constructors?

    试试这个——它包含了你的构造函数的修复并避免了其他线程指出的问题:

    public abstract class Tile{
    
        public int x;
        public int y;
        public int z;
    
        protected Color color;
        protected float friction;
        protected float bounce;
        protected boolean liquid;
    
        public Tile(int x, int y, int z){
            this.x = x;
            this.y = y;
            this.z = z;
        }
    }
    
    
    public class TestTile extends Tile{
    
        // You're missing this.
        public TestTile(int x, int y, int z)
        {
            super(x, y, z);
            this.init();
        }
    
        protected void init(){
            color = Color.RED;
            friction = 0.1f;
            bounce = 0.2f;
            liquid = false;
        }
    }
    

    【讨论】:

    • OP 的Tile“构造函数”有一个返回类型(void),所以它根本不是构造函数。
    • 是的,我对“看到”我认为应该存在的东西而不是存在的东西感到内疚。但是我的 ctor 评论仍然是中肯的,即使进行了更正。
    【解决方案3】:

    首先Tile只有一个带x、y、z参数的构造函数,没有默认构造函数,所以你必须在TestTile构造函数中调用super(x, y, z)。正如slandau 所说,“构造函数”的void 返回类型错误。

    TestTile需要声明参数或传递默认值:

    public TestTile(int x, int y, int z) {
      super(x, y, z);
    }
    
    public TestTile() {
      super(0, 0, 0);
    }
    

    在Java中,在构造函数中调用抽象方法有很多风险,另见here,实例未正确初始化。您只能安全地调用静态方法(这在此处不起作用)。

    public TestTile(int x, int y, int z) {
      super(x, y, z);
      color = Color.RED;
      friction = 0.1f;
      bounce = 0.2f;
      liquid = false;
    }
    

    或者您需要在派生类中调用私有方法(从Tile 中删除抽象init()):

    public TestTile(int x, int y, int z) {
      super(x, y, z);
      init();
    }
    
    private void init() {
      color = Color.RED;
      friction = 0.1f;
      bounce = 0.2f;
      liquid = false;
    }
    

    您确定成员在这里是正确的实施方式吗?也许抽象方法(getter)在这里声明一个行为并在子类中实现它可能会更好?

    public abstract class Tile {
      public int x;
      public int y;
      public int z;
    
      public Tile(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
      }
    
      public abstract Color getColor();
      public abstract float getFriction();
      public abstract float getBounce();
      public abstract boolean isLiquid();
    }
    
    public class TestTile extends Tile {
    
      public TestTile(int x, int y, int z) {
        super(x, y, z);
      }
    
      public Color getColor() {
        return Color.RED;
      }
    
      public float getFriction() {
        return 0.1f;
      }
    
      public float getBounce() {
        return 0.2f;
      }
    
      public boolean isLiquid() {
        return false;
      }
    
    }
    

    【讨论】:

      【解决方案4】:

      构造函数不是继承的,所以创建TestTile对象时不会调用Tile的三参数构造函数。您需要从 TestTile 构造函数中显式调用三参数 Tile 构造函数,就像您说的那样尝试过,但是对 super(x,x,x) 的调用必须是 TestTile 构造函数的第一条语句。

      就像 Matt Ball 所说,在您删除 void 返回类型之前,您的 Tile“构造函数”并不是真正的构造函数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多