Previous Table of Contents Next


Note that the abstract methods in Shape have a semicolon right after their parentheses. There are no curly braces, and no method body is defined. Using the classes defined in Example 14.21, we can now write code like this:

   Shape[] shapes = new Shape[3];
       // Create an array to hold shapes.
   shapes[0] = new Circle(2.0);
       // Fill in the array...
   shapes[1] = new Rectangle(1.0, 3.0);
   shapes[2] = new Rectangle(4.0, 2.0);
   double total_area = 0;
   for(int i = 0; i < shapes.length; i++)
       total_area += shapes[i].area();
       // Compute the area of the shapes.

There are two important points to notice here:

  Subclasses of Shape can be assigned to elements of an array of Shape. No cast is necessary.
  You can invoke the area() and circumference() methods for Shape objects, even though Shape does not define a body for these methods, because Shape declared them abstract. If Shape did not declare them at all, the code would cause a compilation error.

14.3.9.2. Interfaces

Let’s extend our shapes package further. Suppose we now want to implement a number of shapes that can be drawn on the screen. We could define an abstract DrawableShape class, and then implement various subclasses of it, such as DrawableCircle, DrawableRectangle, and so on. This would work fine.

But suppose we want our drawable shape types to support the area() and circumference() methods. We don’t want to have to re-implement these methods, so we’d like to make DrawableCircle a subclass of Circle, for example, and DrawableRectangle a subclass of Rectangle. But classes in Java can only have one superclass. If DrawableCircle extends Circle, then it cannot also extend DrawableShape!26


26C++ allows classes to have more than one superclass, using a technique known as “multiple inheritance.” Multiple inheritance opens up a can of worms, so Java replaces it with what many believe is a more elegant solution.

Java’s solution to this problem is called an interface. An interface looks a lot like an abstract class, except that it uses the keyword interface instead of the words abstract and class. Example 14.22 shows an interface named Drawable.

EXAMPLE 14.22. An interface definition.

   public interface Drawable {
       public void setColor(Color c);
       public void setPosition(double x, double y);
       public void draw(DrawWindow dw);
   }

While an abstract class may define some abstract methods and some non-abstract methods, all the methods defined within an interface are implicitly abstract. We’ve omitted the abstract keyword in this example, but it is perfectly legal to use it if you want to belabor the abstractness of interface methods. A further restriction on interfaces is that any variables declared in an interface must be static and final—that is, they must be constants.

So what can we do with an interface? Just as a class extends its superclass, it also optionally implements an interface. implements is a Java keyword that can appear in a class declaration following the extends clause. implements should be followed by the name of the interface that the class implements. In order to implement an interface, a class must first declare the interface in an implements clause, and then it must provide an implementation (i.e., a body) for all of the abstract methods of the interface.27


27This is the real difference between multiple inheritance in C++ and interfaces in Java. In C++, a class can inherit method implementations from more than one superclass. In Java, a class can inherit actual implementations only from one superclass. It can inherit additional abstract methods from interfaces, but it must provide its own implementation of those methods. It is rare, however, to actually be able to use C++ multiple inheritance to inherit useful, non-trivial implementations from more than one class. The elegance and simplicity of Java’s interface more than compensate for the inability to inherit implementations from more than one class.

Example 14.23 shows how we can define a DrawableRectangle class that extends our Rectangle class and implements the Drawable interface we defined in Example 14.22. The example assumes that a Color class and a DrawWindow class are defined elsewhere, and that DrawWindow knows how to convert floating-point coordinates to pixel coordinates and knows how to draw primitive shapes.

EXAMPLE 14.23. Implementing an interface.

   public class DrawableRectangle extends Rectangle
           implements Drawable {
       // New instance variables
       private Color c;
       private double x, y;
       // A constructor
       public DrawableRectangle(double w, double h)
           { super(w, h); }
       // Here are implementations of the Drawable methods.
       // We also inherit all the public methods of Rectangle.
       public void setColor(Color c) { this.c = c; }
       public void setPosition(double x, double y)
           { this.x = x; this.y = y; }
       public void draw(DrawWindow dw) {
           dw.drawRect(x, y, w, h, c);
       }
   }


Previous Table of Contents Next