Chapter 8: Inheritance


In the last chapter, you learned that objects are instances of classes, containing data and methods defined by the class. This chapter will present two object-oriented concepts that will greatly enhance what you can do with objects. At first glance, inheritance and constructors do not seem to have much to do with each other. However, by the end of this chapter, you will see that a class's constructors are intimately related to that class's inheritance hierarchy.

Superclasses and Subclasses

You already know that a class has data and methods. You provide a class with these features by writing the code that defines the class. Now it's time to learn another way that a class can get data and methods: inheritance.

Inheritance is how a class can get data and methods that are defined in a different class. For this mechanism to work, the two classes must have a special relationship with each other: one must be a superclass of the other, which must be a subclass of the first. In this section, you'll learn what this relationship means.

Let's start with an example. Suppose you are writing a Java program to support the personnel department of the company. You decide that you should create two classes to represent the employees: Worker and Manager. These classes have some similarities and some differences. Here are some of the similarities:

  • Workers and managers both have employee identification numbers, so both classes have an int called id.

  • Workers and managers both need to get paid, so both classes have a float called salary and a method called printCheck. (The details of creating a method that prints checks are beyond the scope of this book, but it seems only fair that everybody should get a check.)

Now here are some of the differences:

  • Managers have workers who report to them, so the Manager class has an array of Worker objects called workers. Workers don't need this, because nobody reports to them.

  • Workers might or might not be eligible for overtime pay, so the Worker class has a boolean called getsOvertime. Managers are never eligible for overtime, so the Manager class does not need this data field.

Of course, a realistic program would have many more data fields and methods in each class, but this is enough to demonstrate the power and usefulness of inheritance. The Worker class looks like this:

public class Worker {   int      id;   float    salary;   boolean  getsOvertime;   void printCheck()   {     // Lots of intricate     // check-printing code     // goes here.   } }

And the Manager class looks like this:

public class Manager {   int      id;   float    salary;   Worker[] workers;   void printCheck()   {     // Same intricate     // check-printing code     // goes here.   } }

Despite their differences, these classes have a lot in common. The most worrisome common feature is the printCheck() method.

Note

Notice the empty parentheses after the method name. This is a common practice when writing about a method. It specifies that you're talking about a method rather than a variable or a class.

printcheck() is worrisome because it appears in identical forms in two places. Duplication of code should be avoided, because code is never frozen in time. Code evolves. Over the lifetime of a program, bugs are found and new features are required. The process of fixing bugs and adding features is called maintenance, and every program requires it. If a method appears in identical forms in two places, every change must be made twice, and the risk of introducing errors rises dramatically.

It is not surprising that workers and managers share some common features. They are both categories of employees. And here we find a simple but profound truth about the way we humans observe our world.

The previous chapter presented classes as programmatic representations of mental categories, such as "triangle" or "dog." Object-oriented programming is a very human approach to writing software, because our minds are good at creating categories for the things we experience in daily life. No doubt all animal species do this to some extent, with categories like "food" and "threat" and "safe place to sleep." People do it best of all.

People are so good at creating mental categories that we take the process one step further. We don't just imagine categories of things. With our talent for abstract thinking, we can imagine categories of categories! So the "triangle" category is one member of a larger mental concept that we might call "shapes." Other members of this supercategory are "squares" and "rectangles." Similarly, the "dog" category belongs to the supercategory "mammals," which in turn belongs to its own supercategory: "animals."

The Swedish philosopher Carl Linnaeus organized all living species into a hierarchy of supercategories with seven levels. This organization is still in use among biologists. If you've ever had to memorize "kingdom, phylum, class, family, order, genus, species" for a biology class, you were studying Linnaeus' hierarchy. His structure was more detailed than our "animal, mammal, dog" hierarchy. You can't really say that either hierarchy is more or less correct, though. Each one is appropriate for certain tasks.

Well, enough philosophy. The point is that it's natural to think about hierarchies of categories, and Java supports this way of thinking. Let's see how this is done.

Inheritance from Superclasses

In Java, a category is represented by a class. A supercategory (if you will continue to permit the use of this made-up word) is represented by a superclass. Superclass is a real word, and so is its opposite: subclass. Every class can have one superclass. That superclass in turn can have its own superclass, and so on. A class may not have multiple superclasses, but multiple subclasses are allowed.

The extends keyword is used to denote the superclass/subclass relationship. To see how this works, let's continue the personnel example from the previous chapter. Right before the philosophical digression, you learned that workers and managers are both categories of employees. You will now create an Employee class that will contain all the shared functionality of workers and managers.

In Java, every class is capable of being a superclass, and you don't have to do anything special in the class definition of a class that will have subclasses. (The special work, as you'll soon see, comes when you define the subclasses.) So the superclass looks like this:

public class Employee {   int      id;   float    salary;   void printCheck()   {     // The same intricate     // check-printing code     // goes here.   } }

Now let's create the Worker subclass:

public class Worker extends Employee {   boolean  getsOvertime; }

The class name is followed by the extends keyword, which is followed by the class's superclass. That's all we need to do! This works because in Java, there are two ways for a class to have a variable or method:

  • The variable or method can be defined in the class.

  • The variable or method can be defined in the class's superclass.

This very simple Worker class just defines a single variable. But its superclass (Employee) defines the variables id and salary, as well as the method printCheck(), so Worker also has those variables and that method. We say that Worker inherits id, salary, and printCheck() from its superclass.

The Manager class is also simple:

public class Manager extends Employee {   Worker[] workers; }

Again, Manager inherits id, salary, and printCheck() from its superclass. The situation is diagrammed in Figure 8.1.


Figure 8.1: A Simple inheritance hierarchy

Figure 8.1 shows that Employee is the superclass of both Worker and Manager. Employee itself does not seem to have a superclass, but in Java, every class you define has a superclass, even if you don't explicitly declare one with the extends keyword. Java provides a class named Object, which is the ultimate ancestor of every class. A class that does not explicitly extend something else extends Object.

The Inherit Lab animated illustration lets you create your own class hierarchy diagrams, so that you can see how variables and methods are inherited. To run the program, type java inherit.InheritLab. You will see a display that shows a three-level class hierarchy, as shown in Figure 8.2.

click to expand
Figure 8.2: Inherit Lab

At the top of the diagram is the Object class. Object has two subclasses, called Class1 and Class4. Each of those classes has two subclasses. The classes are color-coded based on their level in the hierarchy.

At first the classes are boring. Their names don't mean anything, and they don't have any data or methods. But if you left-click on any class, you'll get a pop-up menu that lets you add a subclass, delete the class, or edit the class. (You can't delete or edit Object, since its definition is beyond your control.) First, try adding and deleting classes. Then try editing a class. When you select Edit in the pop-up menu, you get a dialog box that lets you change the name of the class, or add or delete data and methods. The dialog box is shown in Figure 8.3.


Figure 8.3: Inherit Lab's class-editing dialog box

Try adding a variable to one of the classes in the blue level, just below Object. Type a name into the Add Data text field, and then click the Add Data button. Then click Apply. The edit dialog box will go away so that you can see the inheritance diagram. The variable you've added will be seen in the box for the class you edited, and also in all of that class's subclasses, illustrating inheritance of data. You can do the same with methods. Notice that data and methods are color-coded to tell you which class they were defined in.

In the File menu, click on Scenarios. Then look at the two canned hierarchies, which represent animals and transportation. In the Transportation scenario, the bottom-level classes (Car, Bicycle, etc.) inherit from two levels of superclass, as well as from Object. Sophisticated object-oriented programs can have fairly deep hierarchies.

In each scenario, add a subclass at the bottom level and observe the inherited data and methods. Try creating a hierarchy from scratch. If you create something interesting, send us a screenshot or a verbal description at GroundUpJava@sgsware.com. We might include it in the next edition. If so, we'll give you credit.

An Inheritance Example

Let's look at an example of inheritance, expanding on the Worker class from the previous section. Worker is a subclass of Employee, which looks like this:

public class Employee {   int      id;   float    salary;   void printCheck()   {     // Whatever.   } }

Let's add a slightly expanded Worker subclass:

 1. public class Worker extends Employee  2. {  3.   boolean  getsOvertime;  4. 5.   void dumpSalary()  6.   {  7.     System.out.println("Salary = " + salary);  8.   }  9. 10.   public static void main(String[] args) 11.   { 12.     Worker dagwood = new Worker(); 13.     dagwood.salary = 44444.44f; 14.     dagwood.dumpSalary(); 15.     dagwood.printCheck(); 16.   } 17. }

Line 7 of the dumpSalary() method and line 13 of the main() method both act as if salary were an ordinary variable of the Worker class... and they're right. The inherited variables of a class are just like its declared variables. The same is true for inherited methods. Line 15 calls dagwood's printCheck() method, which is inherited.

We will return to this example later on in this chapter. First, it's time to learn what really happens when, as on line 12, an object is constructed.




Ground-Up Java
Ground-Up Java
ISBN: 0782141900
EAN: 2147483647
Year: 2005
Pages: 157
Authors: Philip Heller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net