To complete our discussion of object-oriented programming, we will discuss abstract classes and interfaces.
SubsectionA.4.1Abstract Classes
Consider the class hierarchy shown in Figure A.4.1. The Shape class is the parent of the Rectangle and Circle child classes.
A Shape has an x and y location on the screen, you can calculate its area and perimeter, and you can use toString to convert it to a string. Let’s start writing the code for this class.
Everything is going great until we reach the calcArea and calcPerimeter methods. How do we calculate the area and perimeter of a “shape”? We don’t have that sort of problem with circles and rectangles. We know the formulas for calculating their area and perimeter (AKA circumference). But a “shape” really doesn’t exist. If I tell you to draw me a shape, you’ll ask me “which one?” Shapes are an abstraction.
Here’s how we’ll solve the problem: we will use the keyword abstract on the class declaration. This tells Java that we will never directly instantiate a Shape object. If anyone tries something like the following, they will get an error message.
Shape myShape = new Shape(3.0, 4.5);
We will also put the keyword abstract on the declarations of calcArea and calcPerimeter. And, most important, we will provide only the method header—there will be no method body. Listing A.4.3 shows the changed lines:
NoteA.4.4.
If you look closely at the UML diagram in Figure A.4.1, you will see that the class name and the two method names are in italics. This is how you signify an abstract entity in UML.
Now let’s look at the code for the Circle class, which extends Shape. This is not an abstract class—we know how to calculate a circle’s area and perimeter (circumference):
The class can still use the super keyword to call methods in the parent class. Because Shape never defined the body of the calcArea and calcPerimeter methods, the Circle class must override the methods and provide a method body (lines 17–23). If we omit a definition for calcArea, we get this error:
Circle.java:1: error: Circle is not abstract and does not override
abstract method calcArea() in Shape
public class Circle extends Shape {
^
1 error
NoteA.4.6.
Even though you cannot directly instantiate a Shape, you can use polymorphism to create, for example, an array of Shape and assign concrete (non-abstract) objects such as Circle and Rectangle to its elements, as in Listing A.4.7.
SubsectionA.4.2Interfaces
Let’s look at the simplified UML diagram showing the inheritance for the Bicycle, ElectricBicycle, and CargoBicycle classes from Exercise A.3.1.
What if we want to have an electric cargo bicycle? We would like to be able to say something like the following:
public class ElectricCargoBicycle extends ElectricBicycle, CargoBicycle {
// ...
}
This is called multiple inheritance, and its UML diagram would look something like this:
But we can’t! Java does not allow multiple inheritance.
One solution to this problem is to use an interface. It is somewhat like an abstract class, except that it consists of only methods. Listing A.4.10 shows the Electrified interface:
Line 1 is the key here: the class implements the interface. Because this class implements Electrified, it must provide method bodies for all the methods defined in the interface; those are in lines 11–21.
We can now create our ElectricCargoBicycle by using an interface. We extend CargoBicycle and implement Electrified:
Figure A.4.14 shows the relationship among these classes.
NoteA.4.15.
In this particular example, we could also use composition to create a Battery class and put one of those objects into the electric bicycle and electric cargo bicycle classes.
Java makes extensive use of interfaces in much of its class library. For example, the ArrayList class implements these interfaces:
Iterable, which provides methods for enabling iteration via enhanced for loops
Collection, which provides methods for adding and removing elements