Java: 可能错误的抽象 class 实现

Java: possible wrong abstract class impletaion

我选择这个标题是因为我注意到我在抽象的植入上做错了 class 但我还不太确定是什么。

我出于训练目的创建了 MoveAble 抽象 class,并从中创建了 Ball class。我还为 moveAble class 创建了一个 GetPosition() 方法,并在球 class 中使用它。 但是当我在任何球 object 上调用 GetPosition() 时,我得到的是 Moveable Abstract object 的位置变量。

我猜它应该是那样的,但据我了解我们不能使用抽象 class 无论如何,所以我需要能够获得 [=27= 的位置值] class 即使我只在 parents class.

上实现了这个方法

注意:我是初学者 java 程序员。可能有更好的方法来做我所做的,但这就是我想出来的。我想听听你们怎么看,如果你们认为这一切都是歪的,并且有更好的方法来解决所有这些问题,我将很高兴学习它。

可移动class:

public abstract class MoveAble {
    private int[] position = new int[2];
    private int[] velocity = { 1, 1 };

    public int[] getPosition() {
    return position;
    }
    public abstract void move(int width, int height) ;

球class:

public class Ball extends MoveAble{

    private int[] position = new int[2];
    private int[] velocity = { 1, 1 };

    public Ball(int x_position, int y_position) {

        position[0] = x_position;
        position[1] = y_position;
    }

    @Override
    public void move(int width, int height) {
        if (position[0] >  width - 30 || position[0] <  1) {
            velocity[0] *= -1;
        }
        if (position[1] > height - 30 || position[1] <  1) {
            velocity[1] *= -1;
        }
        position[0] += velocity[0];
        position[1] += velocity[1];

    }

问题是您在 class Ball 中(重新)定义了不同的 positionvelocity 字段,从 Ball 和 subclasses,遮盖了从 class MoveAble.

继承的同名字段

从 class Ball 中删除 posiitionvelocity 字段。在您的基础 class MoveAble 中将字段的访问权限更改为 protected,或者提供受保护的方法以允许子classes 访问该数据。

可以使用抽象类型的引用。 (您不能实例化抽象 class。)当您调用它们的方法时,运行时行为是它们指向的底层具体 object 的行为。

抽象 class 可以在已知的方法中提供默认行为。我的猜测是您的摘要 class 提供了您的 Ball child class 完全可以接受的行为。

这就是多态性的意义所在。 Object 面向编程有四个重要的特点:抽象数据类型、封装、多态、继承。确保你完全理解它们。

我认为您的错误是给抽象和具体 class 赋予了相同的成员变量。那重用在哪里?从 Ball 中删除它们并在 parent 中保护它们。不要覆盖 child 中的方法,除非它们提供不同的行为。

在提供的代码中,MoveAble的字段positionvelocityBallwere hidden. As 中,不需要隐藏字段你的情况。如果我们从 Ball 中删除字段 positionvelocity,我们将得到预期的结果。

至于为什么程序会这样做的原因:即使我们隐藏Ball中的字段,方法public int[] getPosition()源于MoveAble 并且在 MoveAble 中,MoveAble 的字段(以及来自它的 superclass 的字段)是可见的。

最后一点:MoveAble 比抽象 class 更能满足接口的条件。 Here is a discussion about when to use interfaces vs. when to use abstract classes

您可以创建一个名为 IMovable 的接口,这样当您只关心动产时就可以通过它的接口引用这个对象...

现在,如果您有跨多个子类型的通用代码,您可以创建一个抽象 class 并在移动中调用其基础 class 代码,就像我在下面所做的那样,然后添加特定的子类型在子类型中移动逻辑......见下文

public class Ball extends AbstractBall {
    public Ball(int x_position, int y_position) {
        super(x_position, y_position);
    }

    public void move(int width, int height) {
        super.move(width, height);
        System.out.println("SUBTYPE-CLASS MOVE IS CALLED");
    }           

        public static void main(String[] args) {
                Ball ball = new Ball(10, 20);
                ball.move(1, 2);
        }
}

abstract class AbstractBall implements IMovable {

    protected int[] position = new int[2];
    protected int[] velocity = { 1, 1 };

    public AbstractBall(int x_position, int y_position) {
        position[0] = x_position;
        position[1] = y_position;
    }

    public void move(int width, int height) {

        System.out.println("BASE-CLASS MOVE IS CALLED");

        if (position[0] >  width - 30 || position[0] <  1) {
            velocity[0] *= -1;
        }
        if (position[1] > height - 30 || position[1] <  1) {
            velocity[1] *= -1;
        }
        position[0] += velocity[0];
        position[1] += velocity[1];
    }           
}

interface IMovable {
    public void move(int width, int height);
}

通过接口引用实例的另一个好处是 eclipse 自动完成功能会将其项目列表限制为仅接口方法。

将其与引用同一对象但通过其继承层次结构时自动完成中显示的方法爆炸进行比较。

哪个更有意义?

import javax.swing.JButton;

public class Demo {

  public static void main(String[] args) {

    IPrintable instance1 = new PrintableButton();
    instance1.print();

    PrintableButton instance2 = new PrintableButton();
    instance2.print();
  }
}

interface IPrintable {
  public void print();
}

class PrintableButton extends JButton implements IPrintable {
  private static final long serialVersionUID = 1L;

  @Override
  public void print() {
    System.out.println("Printable Button");
  }
}