Search code examples
javainheritanceconstructorsubclasssuperclass

Initialize superclass variables (needed in constructor) in a subclass


I'm writing a simple Asteroids-clone game, using Swing to display the graphics. I'm kind of following Derek Banas' tutorials, but decided to expand on my own.

The original idea is that every graphic element in the game (i.e. the Asteroids, the Spaceship and the Bullets) extends the Polygon class. Their constructor would look something like this:

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

And it will be similar for the other graphic elements.

EDIT: The key element here is that the two arrays don't store the actual coordinates of the objects, but their position relative to their centre, whose coordinates are double-type class-variable. Thus, the arrays describe just the shape of the object, while the subclass move() method will affect the centre's coordinates. The class responsible for the actual drawing will call the move() method and then apply an affine transform to move and rotate the shape (according to a properly defined angle parameter). I'm doing this to avoid precision problems related to dealing with double arithmetic.

Now, since the elements share a lot of "equal" variables (their centre coordinates, which I need in order to translate them with an affine transform, their speed components etc...) and methods (getters and setters, move() methods, etc...) I thought about making them be the extension of an abstract class - say, GameShape - which holds all these common methods and variables. GameShape would now be the one extending Polygon directly:

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

Then, I'd want to assign the desired value to polyXArray and polyYArray when I define the different subclasses in order to draw the different shapes I need, but I haven't been able to find a way to do it.

I do want those variable to be static because they are specific properties of the single classes, and I wouldn't want to pass them as a parameter every time I instantiate a new object.

My situation is very similar to the one described in this question, but the proposed solution doesn't seem to work, since I need those very variables in the constructor. Is there a way to get over - or around - this problem? Regardless of the procedure, my main aim is to have a superclass common to all the graphic elements, in order to avoid tens of lines of copy-pasted code.


Solution

  • EDIT:

    As VGR stated in the comments, this will not compile. So, we will have to change the implementation a little, namely, we will use the HAVE relationship instead of IS relationship :-)

    First of all, do not make the poly array fields static. If you do, they will be the same for all of the subclasses as well, so what is the point?

    Secondly, use a template method design pattern here. Your class will look something like this:

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

    Every extending class will have to override this method and you can instantiate the arrays for each of the classes in each of the method overrides.

    Also, a word about the IS-HAVE relationship problem - what you presented in your example is a IS relationship, in which the GameShape object IS a Polygon, hence the need to call the super constructor and the problem with that. In my solution this is replaces by a HAVE relationship, in which the GameShape object HAS a Polygon object inside, accessed with a getPolygon() method. This allows you to have lots of additional flexibility :-)