Java Reference
In-Depth Information
It is time to use your
abstract Shape
class along with its concrete subclasses
Rectangle
and
Circle
. Note that
the only restriction that is applied to an
abstract
class, when it is used in code, is that you cannot create its objects.
Apart from this restriction, you can use it the same way you can use a concrete class. For example, you can declare a
variable of an
abstract
class type; you can call methods of the
abstract
class using that variable, etc. How do you call
a method on a
Shape
variable, if you cannot create an object of the
Shape
class? This is a good point. Let's consider the
following snippet of code:
// Upcasting at work
Shape s = new Rectangle(2.0, 5.0);
// Late binding at work. s.getArea() will call the getArea() method of
// the Rectangle class.
double area = s.getArea();
If you look at the above code, it makes sense. The first statement creates a
Rectangle
and assigns its reference to a
variable of the
Shape
class, which is a simple case of upcasting. In the second statement, you are calling the
getArea()
method using the
s
variable. The compiler only verifies the existence of the
getArea()
method in the
Shape
class,
which is the declared type of the
s
variables. The compiler does not care whether the
getArea()
method in the
Shape
class is incomplete (
abstract
) or not. It does not care if the
getArea()
method is
abstract
in the
Shape
class because
it is an instance method and it knows that late binding at runtime will decide which code for the
getArea()
method
will be executed. All it cares about is the existence of a method declaration of the
getArea()
method. At runtime,
the late binding process finds that the variable
s
is referring to a
Rectangle
and it calls the
getArea()
method of
the
Rectangle
class. Is it not like having one's cake and eating it too. Have an
abstract
class (incomplete class) and
use it too? If you look at the above two lines of code, you would find that these magical two statements involve so
many concepts of object-oriented programming: abstract class, abstract method, upcasting, method overriding, late
binding, and runtime polymorphism. All of these features are involved in the above two statements to giving you the
ability to write generic and polymorphic code.
Consider a
ShapeUtil
class as shown in Listing 16-39.
Listing 16-39.
A ShapeUtil Class That Has Utility Methods to Draw Any Shapes and Print Details About Them
// ShapeUtil.java
package com.jdojo.inheritance;
public class ShapeUtil {
public static void drawShapes(Shape[] list) {
for (int i = 0; i < list.length; i++) {
// Draw a shape, no matter what it is
list[i].draw(); // Late binding
}
}
public static void printShapeDetails(Shape[] list) {
for (int i = 0; i < list.length; i++) {
// Gather details about the shape
String name = list[i].getName(); // Late Binding
double area = list[i].getArea(); // Late binding
double perimeter = list[i].getPerimeter(); // Late binding