Abstract Classes

The draw method now involves a degree of reuse, but clearly the first two lines of the draw method look very similar from sub-class to sub-class. It would be nice to remove draw from the sub-classes entirely, and provide a generic implementation in Shape.

Before working out how to achieve this, you will deal with another issue that will turn out to be related. It is possible for external classes to create instances of the Shape class:

Shape s1 = new Shape();

This clearly makes no sense: Shapes cannot exist in their own right, they are an abstract concept, and need to be realized as squares, circles, triangles etc. In order to prevent Shapes being instantiated they can therefore be declared as abstract:

public abstract class Shape {

If you try to instantiate the Shape class using the new keyword you will now receive a compilation error.

The abstract keyword is the mirror opposite of the final keyword. When the final keyword is used on a class definition, the class cannot be extended. When the abstract keyword is used on a class definition, the class must be extended. For this reason it is not possible to declare a class both abstract and final.

You will now move the remainder of the draw method from the sub-classes to the parent class. Therefore, change the draw method on Shape as follows:

public void draw() {

    System.out.println("I am drawing a square");

    System.out.printf("I have %d sides\n", sides);   

    System.out.printf("My fill color is %s\n", getFillColor());

    System.out.printf("My line color is %s\n", getLineColor());

}

This will result in a compilation error due to the fact that the sides field is not visible to Shape. Don’t worry about the compilation errors for now; you will deal with this shortly.

After changing this code, remove the draw method entirely from the sub-classes such as Square.

You will first deal with the compilation error in this method. This stems from the fact that the parent class is attempting to access a field called sides: this is only defined on sub-classes, so is not available to the parent-class.

This is nothing to do with the fact that the sides field is private: a parent-class cannot access any fields or methods on its sub-classes; inheritance only works in one direction.

The way around this is for the Shape class to declare that any class that extends it must provide a method called getSides. The Shape class does not need to define the code for this method; it only needs to define its signature. Therefore, add the following line to Shape:

public abstract int getSides();

Notice the use of the abstract keyword: when a method is defined as abstract, only its signature needs to be provided (in fact it is not possible to define an implementation).

As soon as this line is added to Shape, all sub-classes of Shape must implement this method, otherwise the program will not compile.

Notice that Shape does not care how its sub-classes implement this method, or what the implementation does: it only cares that an implementation is provided, and that this implementation returns an int.

You can now change the draw method in Shape as follows:

public void draw() {

       System.out.println("I am drawing a square");

       System.out.printf("I have %d sides\n", getSides()); 

       System.out.printf("My fill color is %s\n", getFillColor());

       System.out.printf("My line color is %s\n", getLineColor());

}

The Shape class is able to invoke the getSides method because it has certainty that any sub-class will provide an implementation of this method. Because the sub-classes do already implement this there is nothing for you to do.

It is only possible to define abstract methods in abstract classes.

//

Any class with an abstract method must be declared abstract, although it is possible for a class to be abstract even though it has no abstract methods.

There would obviously be problems if it were possible for a non-abstract class to include abstract methods. When the class was instantiated it would contain undefined methods.

The whole purpose of the abstract keyword is to pass the buck. It is a way of saying I need this particular method implemented, although I myself do not know how to implement it.

It is also possible to have multiple levels of abstract classes. For instance, if Square declared itself as abstract it would not need to provide an implementation of getSides (although it could if it chose to). The only rule is that all abstract methods must be implemented by the time you reach the first non-abstract class in the class hierarchy.

The draw method still has one remaining problem: it always prints out "I am drawing a square", even if the class that is instantiated is a Triangle. In order to solve this I will add another abstract method to Shape called getType, and then use it in the draw method: the full implementation of Shape will be as follows:

package shapes;

public abstract class Shape {

    private String fillColor;

    private String lineColor;

 

    public Shape() {}

     

    public abstract int getSides();

     

    public abstract String getType();

     

    public void draw() {

        System.out.printf("I am drawing a %s\n", getType());

         System.out.printf("I have %d sides\n", getSides());     

         System.out.printf("My fill color is %s\n", getFillColor());

         System.out.printf("My line color is %s\n", getLineColor());

    }

    protected String getFillColor() {

        return fillColor;

    }

    public void setFillColor(String fillColor) {

        this.fillColor = fillColor;

    }

    protected String getLineColor() {

        return lineColor;

    }

    public void setLineColor(String lineColor) {

        this.lineColor = lineColor;

    }

}

As soon as the getType method is added to Shape, any sub-classes extending it will fail to compile. The easiest way to solve this is to click on the error beside the class definition of each sub-class and choose “Add unimplemented methods”, as seen in figure 12-1:

Figure 12-1

Simply change the stub it creates to return an appropriate value for each sub-class:

@Override

public String getType() {

      return "Square";

}

Notice how Eclipse has added a piece of text above the method:

@Override

This is called an annotation, and is optional in this context. I will cover annotations in more detail later in the book, but for now think of them as tags that can add extra meaning to a piece of code. In this particular case the annotation is telling the compiler “I think I am overriding a method called getType, so please check that there really is a method with this signature on my super-class”.

Annotations are used for a wide variety of tasks in Java at compile time, build time and run-time. If you choose to remove this annotation nothing will change in the functionality of the class, but it won’t have the extra compile-time check to ensure this method really is overriding a method in its parent.

The Square class can now been reduced to this:

package shapes;

 

public final class Square extends Shape {

    private final int sides = 4;

   

    public Square() {}

    @Override

    public int getSides() {

        return sides;

    }

    @Override

    public String getType() {

        return "Square";

    }

}

 

Once getType is implemented in all sub-classes, you can change the Main program as follows:

package shapes;

 

public class Main {

 

    public static void main(String[] args) {

        Square s = new Square();

         s.setFillColor("Blue");

         s.setLineColor("Red");

         s.draw();

         Triangle t = new Triangle();

         t.setFillColor("Green");

         t.setLineColor("Orange");

         t.draw();

    }

}

When executed, this will print out the following:

I am drawing a square

I have 4 sides

I am drawing a Square

I have 4 sides

My fill color is Blue

My line color is Red

I am drawing a triangle

I have 3 sides

A Software Engineer Learns Java and Object Orientated Programming
titlepage.xhtml
part0000_split_000.html
part0000_split_001.html
part0000_split_002.html
part0000_split_003.html
part0000_split_004.html
part0000_split_005.html
part0000_split_006.html
part0000_split_007.html
part0000_split_008.html
part0000_split_009.html
part0000_split_010.html
part0000_split_011.html
part0000_split_012.html
part0000_split_013.html
part0000_split_014.html
part0000_split_015.html
part0000_split_016.html
part0000_split_017.html
part0000_split_018.html
part0000_split_019.html
part0000_split_020.html
part0000_split_021.html
part0000_split_022.html
part0000_split_023.html
part0000_split_024.html
part0000_split_025.html
part0000_split_026.html
part0000_split_027.html
part0000_split_028.html
part0000_split_029.html
part0000_split_030.html
part0000_split_031.html
part0000_split_032.html
part0000_split_033.html
part0000_split_034.html
part0000_split_035.html
part0000_split_036.html
part0000_split_037.html
part0000_split_038.html
part0000_split_039.html
part0000_split_040.html
part0000_split_041.html
part0000_split_042.html
part0000_split_043.html
part0000_split_044.html
part0000_split_045.html
part0000_split_046.html
part0000_split_047.html
part0000_split_048.html
part0000_split_049.html
part0000_split_050.html
part0000_split_051.html
part0000_split_052.html
part0000_split_053.html
part0000_split_054.html
part0000_split_055.html
part0000_split_056.html
part0000_split_057.html
part0000_split_058.html
part0000_split_059.html
part0000_split_060.html
part0000_split_061.html
part0000_split_062.html
part0000_split_063.html
part0000_split_064.html
part0000_split_065.html
part0000_split_066.html
part0000_split_067.html
part0000_split_068.html
part0000_split_069.html
part0000_split_070.html
part0000_split_071.html
part0000_split_072.html
part0000_split_073.html
part0000_split_074.html
part0000_split_075.html
part0000_split_076.html
part0000_split_077.html
part0000_split_078.html
part0000_split_079.html
part0000_split_080.html
part0000_split_081.html
part0000_split_082.html
part0000_split_083.html
part0000_split_084.html
part0000_split_085.html
part0000_split_086.html
part0000_split_087.html
part0000_split_088.html
part0000_split_089.html
part0000_split_090.html
part0000_split_091.html
part0000_split_092.html
part0000_split_093.html
part0000_split_094.html
part0000_split_095.html
part0000_split_096.html
part0000_split_097.html
part0000_split_098.html
part0000_split_099.html
part0000_split_100.html
part0000_split_101.html
part0000_split_102.html
part0000_split_103.html
part0000_split_104.html
part0000_split_105.html
part0000_split_106.html
part0000_split_107.html
part0000_split_108.html
part0000_split_109.html
part0000_split_110.html
part0000_split_111.html
part0000_split_112.html
part0000_split_113.html
part0000_split_114.html
part0000_split_115.html
part0000_split_116.html
part0000_split_117.html
part0000_split_118.html
part0000_split_119.html
part0000_split_120.html
part0000_split_121.html
part0000_split_122.html
part0000_split_123.html
part0000_split_124.html
part0000_split_125.html
part0000_split_126.html
part0000_split_127.html
part0000_split_128.html
part0000_split_129.html
part0000_split_130.html
part0000_split_131.html
part0000_split_132.html
part0000_split_133.html
part0000_split_134.html
part0000_split_135.html
part0000_split_136.html
part0000_split_137.html
part0000_split_138.html
part0000_split_139.html