3.7. Abstract Classes and MethodsIn Example 3-4, we declared our Circle class to be part of a package named shapes. Suppose we plan to implement a number of shape classes: Rectangle, Square, Ellipse, triangle, and so on. We can give these shape classes our two basic area( ) and circumference() methods. Now, to make it easy to work with an array of shapes, it would be helpful if all our shape classes had a common superclass, Shape. If we structure our class hierarchy this way, every shape object, regardless of the actual type of shape it represents, can be assigned to variables, fields, or array elements of type Shape. We want the Shape class to encapsulate whatever features all our shapes have in common (e.g., the area() and circumference( ) methods). But our generic Shape class doesn't represent any real kind of shape, so it cannot define useful implementations of the methods. Java handles this situation with abstract methods. Java lets us define a method without implementing it by declaring the method with the abstract modifier. An abstract method has no body; it simply has a signature definition followed by a semicolon.[8] Here are the rules about abstract methods and the abstract classes that contain them:
There is an important feature of the rules of abstract methods. If we define the Shape class to have abstract area() and circumference( ) methods, any subclass of Shape is required to provide implementations of these methods so that it can be instantiated. In other words, every Shape object is guaranteed to have implementations of these methods defined. Example 3-5 shows how this might work. It defines an abstract Shape class and two concrete subclasses of it. Example 3-5. An abstract class and concrete subclassespublic abstract class Shape {
public abstract double area(); // Abstract methods: note
public abstract double circumference(); // semicolon instead of body.
}
class Circle extends Shape {
public static final double PI = 3.14159265358979323846;
protected double r; // Instance data
public Circle(double r) { this.r = r; } // Constructor
public double getRadius() { return r; } // Accessor
public double area() { return PI*r*r; } // Implementations of
public double circumference() { return 2*PI*r; } // abstract methods.
}
class Rectangle extends Shape {
protected double w, h; // Instance data
public Rectangle(double w, double h) { // Constructor
this.w = w; this.h = h;
}
public double getWidth() { return w; } // Accessor method
public double getHeight() { return h; } // Another accessor
public double area() { return w*h; } // Implementations of
public double circumference() { return 2*(w + h); } // abstract methods.
}Each abstract method in Shape has a semicolon right after its parentheses. They have no curly braces, and no method body is defined. Using the classes defined in Example 3-5, we can now write code such as: 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 shapesNotice two important points here:
|