在子类

时间:2015-10-07 20:52:55

标签: java inheritance constructor subclass superclass

我正在编写一个简单的小行星克隆游戏,使用Swing来显示图形。我有点跟随Derek Banas' tutorials,但决定自己扩展。

最初的想法是游戏中的每个图形元素(即小行星,宇宙飞船和子弹)都扩展了Polygon类。他们的构造函数看起来像这样:

public class SpaceShip extends Polygon {

    //x and y coordinates
    public static int[] polyXArray = {...};
    public static int[] polyYArray = {...};

    //other class variables
    {...}

    public SpaceShip() {

        super(polyXArray, polyYArray, polyXArray.length);
    }
}

它与其他图形元素类似。

编辑:这里的关键要素是两个数组不存储对象的实际坐标,但是它们相对于中心的位置,其坐标为double - 类型为类变量。因此,数组仅描述对象的形状,而子类move()方法将影响中心的坐标。负责实际绘图的类将调用move()方法,然后应用仿射变换来移动和旋转形状(根据正确定义的角度参数)。我这样做是为了避免与处理double算术相关的精度问题。

现在,由于这些元素共享了很多"等于"变量(它们的中心坐标,我需要它们用仿射变换转换它们,它们的速度组件等......)和方法(getter和setter,move()方法等...)我考虑过制作它们是抽象类的扩展 - 比如GameShape - 它包含所有这些常用的方法和变量。 GameShape现在将是直接延长Polygon的那个:

public abstract class GameShape extends Polygon {

        //x and y coordinates, still unassigned
        public static int[] polyXArray, polyYArray;

        //other class variables
        {...}

        public GameShape() {

            super(polyXArray, polyYArray, polyXArray.length);
        }
}

然后,当我定义不同的子类以便绘制我需要的不同形状时,我想要将所需的值分配给polyXArraypolyYArray,但我还没有'找到了办法。

我确实希望这些变量是静态的,因为它们是单个类的特定属性,每次我实例化一个新对象时,我都不想将它们作为参数传递。

我的情况与this question中描述的情况非常相似,但建议的解决方案似乎不起作用,因为我需要构造函数中的那些变量。有没有办法克服 - 或周围 - 这个问题?无论程序如何,我的主要目标是为所有图形元素提供一个共同的超类,以避免数十行复制粘贴代码。

6 个答案:

答案 0 :(得分:2)

您有一对数组,用于描述特定种类游戏对象的形状。如果不同的游戏对象可以具有不同的形状,则它们不能共享一对数组,如果它们是所有游戏对象类的公共超类的静态属性的话。相同类型的不同对象可以共享同一对数组(假设那些不需要基于每个对象修改),这可能对应于那些数组是静态字段的具体的游戏对象类。但是,在这种情况下,如果您希望这些类的超类能够访问给定游戏对象的正确形状数据,则必须告诉它们这些形状数据是什么。

有两种主要方法可以做到这一点:

  1. 您可以将适当的形状数组传递给超类的构造函数。你说你不想这样做,但我不明白为什么。

  2. 您可以在超类上定义子类应该覆盖的访问器方法,以提供正确的形状数据(这称为模板方法模式)。

答案 1 :(得分:1)

数组字段不能是static,因为不同的形状具有不同的坐标。此外,您不需要在特定子类中使用这些数组,因为它们已经在PolygonGameShape中。

这里或多或少我会写GameShape(尽管我同意@Michael你不需要将polyXArraypolyXArray.length都传递给构造函数)

public abstract class GameShape extends Polygon {

    // I got rid of the array fields as I think they are in Polygon anyway.

    //other class variables
    {...}

    // I added arguments to your constructor.
    public GameShape(int[] polyXArray, int[] polyYArray) {

        super(polyXArray, polyYArray, polyXArray.length);
    }
}

问题是super必须是构造函数的第一行,但你可以使用私有方法来构建数组:

public final class BoringRectangle extends GameShape {

    public BoringRectangle(int left, int right, int top, int bottom) {
        super(xArray(left, right), yArray(top, bottom));
    }

    private static int[] xArray(int left, int right) {
        return new int[] {left, right, right, left};
    }

    private static int[] yArray(int top, int bottom) {
        return new int[] {bottom, bottom, top, top};
    }
}

答案 2 :(得分:1)

this question的解决方案将适用于您的类不会扩展形状,但通过访问者+私有静态字段提供形状。

public abstract class GameObject {
    ...
    public abstract Polygon getShape();

这也有助于避免形状重复。

答案 3 :(得分:1)

如果您确实想要在构造函数中初始化事物,只需调用空super();,然后循环抽象getPolyXArray()getPolyYArray()以提供addPoint

public abstract class GameShape extends Polygon {

    public GameShape() {
        super();

        final int length = getPolyXArray().length;
        for (int i = 0; i < length; i++) {
            addPoint(getPolyXArray()[i], getPolyYArray()[i]);
        }
    }

    public abstract int[] getPolyXArray();
    public abstract int[] getPolyYArray();

    //common stuff...
}


public class Asteroids extends Polygon {
    public int[] getPolyXArray() { return new int[]{1, 2, 3}; }
    public int[] getPolyYArray() { return new int[]{1, 2, 3}; }
}

答案 4 :(得分:0)

我认为polyXArraypolyYArray位于Polygon类;这就是他们所属的地方。因此,重复字段不是一个好主意。另外,摆脱ned来调用super构造函数。我会像这样设计类结构:

public class SquareShape extends Polygon {
    private int size;

    public SquareShape(int x, int y, int size) {
        this.size = size;
        int[] xpoints = new int[4]{
                x - size / 2,
                x - size / 2,
                x + size / 2,
                x + size / 2
        };
        int[] ypoints = new int[4]{
                y - size / 2,
                y + size / 2,
                y + size / 2,
                y - size / 2
        };
        setXArray(xpoints);
        setYArray(ypoints);
    }
}

这样,您可以确保所有SquareShape个对象确实具有方形形状,但您可以自定义应该能够自定义的内容。像位置和大小一样,它不应该是静态共享字段。 setXArraysetYArray应该是位于protected的{​​{1}}种方法。你不希望外面的世界弄乱各个点。不过,您可以添加公共getter。

您可能需要考虑使用复杂Polygon类型的单个数组,而不是两个紧密耦合且依赖的数组。我觉得这会大大简化你项目中的很多任务。

答案 5 :(得分:-1)

编辑:

正如VGR在评论中所述,这将无法编译。所以,我们将不得不稍微改变实现,即我们将使用HAVE关系而不是IS关系: - )

首先,不要将多边形数组字段设为静态。如果这样做,它们对于所有子类也是一样的,那么重点是什么呢?

其次,在此处使用模板方法设计模式。你的课将看起来像这样:

public abstract class GameShape {

        //x and y coordinates, still unassigned
        public int[] polyXArray, polyYArray;

        private Polygon polygon;

        //other class variables
        {...}

        public GameShape() {
            instantiatePolyArrays();
            this.polygon = new Polygon(polyXArray, polyYArray, polyXArray.length);
        }

        protected abstract void instantiatePolyArrays();

        public final Polygon getPolygon(){
            return this.polygon;
        }
}

每个扩展类都必须重写此方法,并且可以为每个方法覆盖中的每个类实例化数组。

此外,关于IS-HAVE关系问题的一个词 - 您在示例中呈现的是IS关系,其中GameShape对象是Polygon,因此需要调用超级构造函数和问题。在我的解决方案中,它由HAVE关系替换,其中GameShape对象内部有一个Polygon对象,使用getPolygon()方法访问。这使您可以获得更多灵活性: - )