In the beginning, we defined the BasicShape class to contain only an empty paint method. Now it contains some other more interesting methods (getX, setX, etc), but the paint method is still empty:
import java.awt.*; class BasicShape { private Color col; private int x, y; public Color getColor() {return col;} public void setColor(Color c) {col = c;} public int getX() {...} public void setX(int x1) {...} public int getY() {...} public void setY(int y1) {...} public void paint(Graphics g) {} }
If the paint method is just empty, why do we need it anyway? The problem is that the paint code in BasicShapesFrame manipulates shapes. That is, the code for BasicShapesFrame looks something like this:
class BasicShapesFrame { ... public void addBasicShape(BasicShape s) { .. } public void paint(Graphics g) { BasicShape s; ... ... s.paint(g) ... }
Thus we must ensure that every shape has a paint method. One way to do this is to simply define a paint method in the BasicShape class.
But every shape should be painted differently. To paint a rectangle, we use fillRect. To paint an ellipse, we use fillOval. Thus there is nothing that a generic paint method in the BasicShape class can do. Thus we make just an empty method in BasicShape and hope that each subclass will override the method with an appropriate definition. But this is a rather error-prone approach, because if the subclass forgets to redefine the paint method, it will default to the one in BasicShape, which does no painting at all.
So what we want to do it to specify that every shape must have a paint method, without specifying a dummy definition that is actually incorrect for all shapes.
An abstract class is a class that specifies some variables and methods, but leaves others to be specified by its subclasses. The variables and methods that are included are shared by all the subclasses. The methods that are not specified, known as abstract methods, must be specified by any non-abstract subclass. An abstract method is a method that should be applicable to any object of the class, but for which particular objects will have their own definitions.
The paint method is a method that should be applicable to any BasicShape but for which every kind of BasicShape will have its own definition. Thus we make BasicShape an abstract class and paint an abstract method:
import java.awt.*; abstract class BasicShape { private Color col; private int x, y; public Color getColor() {return col;} public void setColor(Color c) {col = c;} public int getX() {...} public void setX(int x1) {...} public int getY() {...} public void setY(int y1) {...} abstract public void paint(Graphics g); }
The other methods are unchanged, because every kind of BasicShape should treat its position and color similarly.
Notice that the syntax for an abstract method is a little bit different from the syntax for an ordinary method. An ordinary method always has "{...}" and no semicolon at the end. An abstract method always has a semicolon at the end, but nothing in braces. When an abstract method is defined in a subclass, the format of the declaration is as before. For example, the definition of Rect is still:
public class Rect extends BasicShape { ... public void paint(Graphics g) { ... } }
Since not all of the methods are specified in an abstract class, you can't make an instance of an abstract class using new. You can only make an instance of a subclass.