abstract class
Classes in a hierarchy are related by the "is-a" relationship. For example, a Nissan is-a Automobile, and a Sentra is-a Nissan. This chapter discusses how reference variables are used for objects from different classes within a hierarchy. Another subject is the idea of an abstract class---a class that cannot be instantiated, but can be the parent of other classes
The
example of this chapter is a hierarchy of greeting cards types. The parent class is Card,
and its children classes are Valentine, Holiday, and Birthday.
A card object will have a greeting() method that writes out a greeting. Each type of card contains an appropriate greeting; the Holiday card says "Season's Greetings," the Birthday card says "Happy Birthday," and the Valentine cards says "Love and Kisses."
In this example, an object must be an instance of one of the three child types: Valentine, Holiday, and Birthday. There will be no such thing as an object that is merely a "Card." The Card class represents the abstract concept of "Card." All actual card objects must be more specific.
For this example, all card objects are one of the three types (Valentine, Holiday, or Birthday) and the parent class Card is used only to group them into a hierarchy. There will never be an object that is just a card. This is useful to do, just as a drug store will have all its various greeting cards in one display, arranged into several categories.
An abstract class in Java is a class that is never instantiated. Its purpose is to be a parent to several related classes. The child classes will inherit from the abstract parent class.
In hierarchy drawings (such as on the previous page), abstract classes are often drawn with dotted lines. An abstract class is defined like this:
abstract class ClassName { . . . . . // definitions of methods and variables } |
Access modifiers such as public can be placed before abstract. Even though it can not be instantiated, an abstract class can define methods and variables that children classes inherit.
In this example implementation, each card class will have its own version of the greeting() method. Since each class has a greeting(), but each one is implemented differently, it is useful to put an abstract greeting() method in the parent class. This says that each child inherits the "idea" of greeting(), but each implementation is different. Here is the class definition of the abstract class Card:
abstract class Card { String recipient; // name of who gets the card public abstract void greeting(); // abstract greeting() method } |
This is a complete definition of this abstract class. Notice the abstract method. Abstract classes can (but don't have to) contain abstract methods. Also, an abstract class can contain non-abstract methods, which will be inherited by the children.
An abstract method has no body. (It has no statements.) It declares an access modifier, return type, and method signature followed by a semicolon. A non-abstract child class inherits the abstract method and must define a non-abstract method that matches the abstract method.
An abstract child of an abstract parent does not have to define non-abstract methods for the abstract signatures it inherits.
Here is a class definition for class Holiday. It is a non-abstract child of an abstract parent:
class Holiday extends Card { public Holiday( String r ) { recipient = r; } public void greeting() { System.out.println("Dear " + recipient + ",\n"); System.out.println("Season's Greetings!\n\n"); } } |
The class Holiday is not an abstract class; objects can be instantiated from it.
abstract class Card { String recipient; public abstract void greeting(); } class Holiday extends Card { public Holiday( String r ) { recipient = r; } public void greeting() { System.out.println("Dear " + recipient + ",\n"); System.out.println("Season's Greetings!\n\n"); } } public class CardTester { public static void main ( String[] args ) { Holiday hol = new Holiday("Santa"); hol.greeting(); } } |
Not everything defined in an abstract classes needs to be abstract. The variable recipient is defined in Card and inherited in the usual way. However, if a class contains even one abstract method, then the class itself has to be declared to be abstract. At left is a program to test the two classes.
This is a complete runable program. I imagine the urge to copy it to NotePad and get it running is irresistable. Remember to call the file "CardTester.java" .
Here is a sample run of the program:
Dear Santa, Season's Greetings!
The advantage of using an abstract class is that you can group several related classes together as siblings, even though none is the parent of the others. Grouping classes together is important in keeping a program organized and understandable. The picture shows this program after its object has been constructed.
It would be nice to deal some other cards. Here is a skeleton for the Birthday class:
class Birthday extends ______________ { int age; public ______________ ( String r, int years ) { recipient = r; age = years; } public void greeting() { System.out.println("Dear " + recipient + ",\n"); System.out.println("Happy " + _______ + "th Birthday\n\n"); } } |
The missing parts have been filled in, below. | ||
Valentine CardHere is the complete birthday card:
The Valentine class is much the same, except for some added passion:
|
Here is a complete program with all three card classes and objects of each type. If you were deficient in greeting cards this year, you might wish to copy this to NotePad and run it a few times.
import java.io.*; abstract class Card { String recipient; public abstract void greeting(); } class Holiday extends Card { public Holiday( String r ) { recipient = r; } public void greeting() { System.out.println("Dear " + recipient + ",\n"); System.out.println("Season's Greetings!\n\n"); } } class Birthday extends Card { int age; public Birthday ( String r, int years ) { recipient = r; age = years; } public void greeting() { System.out.println("Dear " + recipient + ",\n"); System.out.println("Happy " + age + "th Birthday\n\n"); } } class Valentine extends Card { int kisses; public Valentine ( String r, int k ) { recipient = r; kisses = k; } public void greeting() { System.out.println("Dear " + recipient + ",\n"); System.out.println("Love and Kisses,\n"); for ( int j=0; j < kisses; j++ ) System.out.print("X"); System.out.println("\n\n"); } } public class CardTester { public static void main ( String[] args ) throws IOException { String me; BufferedReader input = new BufferedReader( new InputStreamReader(System.in) ); System.out.println("Your name:"); me = input.readLine(); Holiday hol = new Holiday( me ); hol.greeting(); Birthday bd = new Birthday( me, 21 ); bd.greeting(); Valentine val = new Valentine( me, 7 ); val.greeting(); } } |
By using hierarchical organization and inheritance it is easy to add many more card classes and to create a well organized program. This was unthinkable not too many years ago. Here is a sample run of this program:
Your name: Sue Dear Sue, Season's Greetings! Dear Sue, Happy 21th Birthday Dear Sue, Love and Kisses, XXXXXXX
When the main() method has constructed its three objects, the situation is as in the picture. There are three classes that can be instantiated, and one object of each class has been instantiated. Of course, as many objects as you need of each type (except Card) can be instantiated.
The program can't do the following:
. . . . public static void main ( String[] args ) throws IOException { . . . . Card card = new Card() ; // can't instantiate abstract class card.greeting() ; . . . . } |
Because Card is an abstract class, the compiler would say this is a syntax error. However, the following is OK:
. . . . public static void main ( String[] args ) throws IOException { . . . . Card card = new Valentine( "Joe", 14 ) ; // a Valentine is-a Card card.greeting() ; . . . . } |
It is OK to save a reference to a Valentine object in a reference variable of type Card because Valentine is-a Card. You can think of the reference variable card as being a card rack designed to hold any specific instance of a Card.
A reference variable of class "C" can be used with any object that is related by inheritance to class "C". For example, a Card reference variable card2 can hold a reference to a Holiday object, a Valentine object, or a Birthday object. Usually, references to a parent class are used to hold children objects.
Important Point:
When a method is invoked, it is the class of the object (not of the variable) that determines which method is run.
For example:
Card card = new Valentine( "Joe", 14 ) ; card.greeting(); Card card2 = new Holiday( "Bob" ) ; card2.greeting(); Card card3 = new Birthday( "Emily", 12 ) ; card3.greeting();
This will run the greeting() method for a Valentine, then it will run the greeting() method for a Holiday, then it will run the greeting() method for a Birthday. The type of the object in each case determines which version of the method is run