GeometricObject was declared as the superclass for Circle and Rectangle in the preceding chapter. GeometricObject models common features of geometric objects. Both Circle and Rectangle contain the getArea() and getPerimeter() methods for computing the area and perimeter of a circle and a rectangle. Since you can compute areas and perimeters for all geometric objects, it is better to declare the getArea() and getPerimeter() methods in the GeometricObject class. However, these methods cannot be implemented in the GeometricObject class because their implementation is dependent on the specific type of geometric object. Such methods are referred to as abstract methods . After you declare the methods in GeometricObject , GeometricObject becomes an abstract class. The new GeometricObject class is shown in Figure 10.1. In UML graphic notation, the names of abstract classes and their abstract methods are italicized, as shown in Figure 10.1. Listing 10.1 gives the source code for the new GeometricObject class.
1 public abstract class GeometricObject { 2 private String color = "white" ; 3 private boolean filled; 4 private java.util.Date dateCreated; 5 6 /** Construct a default geometric object */ 7 protected GeometricObject() { 8 dateCreated = new java.util.Date(); 9 } 10 11 /** Return color */ 12 public String getColor() { 13 return color; 14 } 15 16 /** Set a new color */ 17 public void setColor(String color) { 18 this .color = color; 19 } 20 21 /** Return filled. Since filled is boolean, 22 so, the get method name is isFilled */ 23 public boolean isFilled() { 24 return filled; 25 } 26 27 /** Set a new filled */ 28 public void setFilled( boolean filled) { 29 this .filled = filled; 30 } 31 32 /** Get dateCreated */ 33 public java.util.Date getDateCreated() { 34 return dateCreated; 35 } 36 37 /** Return a string representation of this object */ 38 public String toString() { 39 return "created on " + dateCreated + "\ncolor: " + color + 40 " and filled: " + filled; 41 } 42 43 /** Abstract method getArea */ 44 public abstract double getArea(); 45 46 /** Abstract method getPerimeter */ 47 public abstract double getPerimeter(); 48 } |
Abstract classes are like regular classes with data and methods, but you cannot create instances of abstract classes using the new operator. An abstract method is a method signature without implementation. Its implementation is provided by the subclasses. A class that contains abstract methods must be declared abstract.
The GeometricObject abstract class provides the common features (data and methods) for geometric objects. Because you don't know how to compute areas and perimeters of geometric objects, getArea and getPerimeter are defined as abstract methods. These methods are implemented in the subclasses. The implementation of Circle and Rectangle is the same as in Listings 9.2 and 9.3 except that they don't have the package statements. The new GeometricObject.java , Circle.java , and Rectangle.java are stored in c:\book . Note that the GeometricObject , Circle , and Rectangle classes in the preceding chapter are placed in a package named chapter9 to avoid naming conflicts with the classes in this chapter.
Note
An abstract method cannot be contained in a nonabstract class. If a subclass of an abstract superclass does not implement all the abstract methods, the subclass must be declared abstract. In other words, in a nonabstract subclass extended from an abstract class, all the abstract methods must be implemented, even if they are not used in the subclass. Also note that abstract methods are non-static. |
Note
An abstract class cannot be instantiated using the new operator, but you can still define its constructors, which are invoked in the constructors of its subclasses. For instance, the constructors of GeometricObject are invoked in the Circle class and the Rectangle class. |
Note
A class that contains abstract methods must be abstract. However, it is possible to declare an abstract class that contains no abstract methods. In this case, you cannot create instances of the class using the new operator. This class is used as a base class for defining a new subclass. |
Note
A subclass can be abstract even if its superclass is concrete. For example, the Object class is concrete, but its subclasses, such as GeometricObject , may be abstract. |
Note
A subclass can override a method from its superclass to declare it abstract . This is very unusual , but is useful when the implementation of the method in the superclass becomes invalid in the subclass. In this case, the subclass must be declared abstract. |
Note
You cannot create an instance from an abstract class using the new operator, but an abstract class can be used as a data type. Therefore, the following statement, which creates an array whose elements are of GeometricObject type, is correct. GeometricObject[] objects = new GeometricObject[ 10 ]; |
You may be wondering whether the abstract methods getArea and getPerimeter should be removed from the GeometricObject class. The following example shows the benefits of retaining them in the GeometricObject class.
This example presents a program that creates two geometric objects, a circle and a rectangle, invokes the equalArea method to check whether the two objects have equal areas, and invokes the displayGeometricObject method to display the objects.
Listing 10.2 gives the solution to the problem. A sample run of the program is shown in Figure 10.2.
1 public class TestGeometricObject { 2 /** Main method */ 3 public static void main(String[] args) { 4 // Declare and initialize two geometric objects 5 GeometricObject geoObject1 = new Circle( 5 ); 6 GeometricObject geoObject2 = new Rectangle( 5 , 3 ); 7 8 System.out.println( "The two objects have the same area? " + 9 equalArea(geoObject1, geoObject2) ); 10 11 // Display circle 12 displayGeometricObject(geoObject1); 13 14 // Display rectangle 15 displayGeometricObject(geoObject2); 16 } 17 18 /** A method for comparing the areas of two geometric objects */ 19 public static boolean equalArea(GeometricObject object1, 20 GeometricObject object2) { 21 return object1.getArea() == object2.getArea(); 22 } 23 24 /** A method for displaying a geometric object */ 25 public static void displayGeometricObject(GeometricObject object) { 26 System.out.println(); 27 System.out.println( "The area is " + object.getArea()); 28 System.out.println( "The perimeter is " + object.getPerimeter()); 29 } 30 } |
The methods getArea() and getPerimeter() defined in the GeometricObject class are overridden in the Circle class and the Rectangle class. The statements (lines 5 “6)
GeometricObject geoObject1 = new Circle( 5 ); GeometricObject geoObject2 = new Rectangle( 5 , 3 );
create a new circle and rectangle, and assign them to the variables geoObject1 and geoObject2 . These two variables are of the GeometricObject type.
When invoking equalArea(geoObject1, geoObject2) (line 9), the getArea method defined in the Circle class is used for object1.getArea() , since geoObject1 is a circle, and the getArea method defined in the Rectangle class is used for object2.getArea() , since geoObject2 is a rectangle.
Similarly, when invoking displayGeometricObject(geoObject1) (line 12), the methods getArea and getPerimeter defined in the Circle class are used, and when invoking displayGeometricObject(geoObject2) (line 15), the methods getArea and getPerimeter defined in the Rectangle class are used. The JVM dynamically determines which of these methods to invoke at runtime, depending on the type of object.
Note that if the getArea and getPerimeter methods were not defined in GeometricObject , you cannot define the equalArea and displayObject methods in this program. So, you now see the benefits of defining the abstract methods in GeometricObject .