3.7. Abstract Classes and MethodsIn Example 34, 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 35 shows how this might work. It defines an abstract Shape class and two concrete subclasses of it. Example 35. 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 35, 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 shapes Notice two important points here:
