The Essence of OOP using Java, Inheritance, Part 2

Baldwin shows you how to use method overriding to cause the behavior of a method inherited into a subclass to be appropriate for an object instantiated from the subclass.

Published:  January 28, 2002
By Richard G. Baldwin

Java Programming Notes # 1606


Preface

This lesson is one of a series of lessons designed to teach you about the essence of Object-Oriented Programming (OOP) using Java.

The first lesson in the group was entitled The Essence of OOP Using Java, Objects, and Encapsulation.  That lesson, and each of the lessons following that one, has provided explanations of certain aspects of the essence of Object-Oriented Programming using Java.  The previous lesson was entitled The Essence of OOP using Java, Inheritance, Part 1.

Necessary and significant aspects

This miniseries will describe and discuss the necessary and significant aspects of OOP using Java.

If you have a general understanding of computer programming, you should be able to read and understand the lessons in this miniseries, even if you don't have a strong background in the Java programming language.

Viewing tip

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different listings while you are reading about them.

Supplementary material

I recommend that you also study the other lessons in my extensive collection of online Java tutorials.  You will find those lessons published at Gamelan.com.  However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there.  You will find a consolidated index at Baldwin's Java Programming Tutorials.

Preview

This lesson builds on the previous lesson.  It is recommended that you study that lesson before embarking on this lesson.

The program discussed in this lesson extends a Radio class to produce a new class that simulates an upgraded car radio containing a tape player.

Method overriding is used to modify the behavior of a method of the Radio class named playStation, to cause that method to behave appropriately when a tape has been inserted into the tape player.

Discussion and Sample Code

Inheriting methods and variables

When you define a class that extends another class, an object instantiated from your new class will contain all of the methods and all of the variables defined in your new class.  The object will also contain all of the methods and all of the variables defined in all of the superclasses of your new class.

The behavior of the methods

The behavior of the methods defined in a superclass and inherited into your new class may, or may not, be appropriate for an object instantiated from your new class.  If those methods are appropriate, you can simply leave them alone.

Overriding to change behavior

If the behavior of one or more methods defined in a superclass and inherited into your new class is not appropriate for an object of your new class, you can change that behavior by overriding the method in your new class.

How do you override a method?

To override a method in your new class, simply reproduce the name, argument list, and return type of the original method in a new method definition in your new class.  Then provide a body for the new method.  Write code in that body to cause the behavior of the overridden method to be appropriate for an object of your new class.

Here is a more precise description of method overriding taken from the excellent book entitled The Complete Java 2 Certification Study Guide, by Roberts, Heller, and Ernest:

"A valid override has identical argument types and order, identical return type, and is not less accessible than the original method.  The overriding method must not throw any checked exceptions that were not declared for the original method."
Any method that is not declared final can be overridden in a subclass.

Overriding versus overloading

Don't confuse method overriding with method overloading.  Here is what Roberts, Heller, and Ernest have to say about overloading methods:

"A valid overload differs in the number or type of its arguments.  Differences in argument names are not significant.  A different return type is permitted, but is not sufficient by itself to distinguish an overloading method."
Car radios with built-in tape players

This lesson presents a sample program that duplicates the functionality of the program named Radio02 discussed in the previous lesson.  A class named Radio is used to define the specifics of objects intended to simulate car radios.

A class named Combo extends the Radio class to define the specifics of objects intended to simulate improved car radios having built-in tape players.

Modification of the superclass

In the program named Radio02 in the previous lesson, it was necessary to modify the superclass before extending it to provide the desired functionality. (The requirement to modify the superclass before extending it seriously detracts from the benefits of inheritance.)

No superclass modification in this lesson

The sample program (named Radio03) in this lesson uses method overriding to provide the same functionality as the previous program named Radio02, without any requirement to modify the superclass before extending it.  (Thus this program is more representative of the benefits available through inheritance than was the program in the previous lesson.)

Overridden playStation method

In particular, a method named playStation, defined in the superclass named Radio, is overridden in the subclass named Combo.

The original version of playStation in the superclass supports only radio operations.  The overridden version of playStation defined in the subclass supports both radio operations and tape operations.

(The behavior of the version of playStation defined in the Radio class is not appropriate for an object of the Combo class.  Therefore, the method was overridden in the Combo class to cause its behavior to be appropriate for objects instantiated from the Combo class.)
A complete listing of the program is shown in Listing 5 near the end of this lesson.

The class named Radio

As usual, I will discuss the program in fragments.

Listing 1 shows the superclass named Radio.  This code is shown here for easy referral.  It is identical to the code for the same class used in the program named Radio01 discussed in an earlier lesson entitled The Essence of OOP using Java, Classes.
 
class Radio{
  protected double[] stationNumber = 
                         new double[5];
                            
  public void setStationNumber(
                int index,double freq){
    stationNumber[index] = freq;
  }//end method setStationNumber

  public void playStation(int index){
    System.out.println(
            "Playing the station at " 
               + stationNumber[index]
               + " Mhz");
  }//end method playStation
  
}//end class Radio

Listing 1

Will override playStation

The class named Combo (discussed below) will extend this class named Radio.  The method named playStation, shown in boldface in Listing 1, will be overridden in the class named Combo.

If you examine the code for the playStation method in Listing 1, you will see that it assumes radio operations only and doesn't support tape operations.  That is the reason that it needs to be overridden. (For example, it doesn't know that it should refuse to play a radio station when a tape is being played.)

The Combo class

Listing 2 shows the beginning of the class definition for the class named Combo.  The Combo class extends the class named Radio.
 
class Combo extends Radio{
  
  private boolean tapeIn = false;
  //---------------------------------//
  
  public Combo(){//constructor
    System.out.println(
           "Combo object constructed");
  }//end constructor
  //---------------------------------//

Listing 2

The tapeIn variable

The most important thing about the code in Listing 2 is the declaration of the instance variable named tapeIn.

(In the program named Radio02 in the previous lesson, this variable was declared in the class named Radio and inherited into the class named Combo.  That was one of the undesirable changes required for the class named Radio in that lesson.)
In this version of the program, the variable named tapeIn is declared in the subclass instead of in the superclass.  Thus, it is not necessary to modify the superclass before extending it.

The constructor

The constructor in Listing 2 is the same as in the previous program named Radio02, so I won't discuss it further.

The overridden playStation method

The overridden version of the method named playStation is shown in Listing 3.

As you can see, this version of the method duplicates the signature of the playStation method in the superclass named Radio, but provides a different body.
 
  public void playStation(int index){
    System.out.println("Play Radio");
    if(!tapeIn){//tapeIn is false
      System.out.println(
          "  Playing the station at " 
               + stationNumber[index]
               + " Mhz");
    }else{//tapeIn is true
      System.out.println(
            "  Remove the tape first");
    }//end if/else
  }//end method playStation

Listing 3

Aware of the tape system

This overridden version of the playStation method is aware of the existence of the tape system and behaves accordingly.

Depending on the value of the variable named tapeIn, this method will either tune and play a radio station, or display a message instructing the user to remove the tape.

Which version of playStation is executed?

When the playStation method is invoked on an object of the Combo class, the overridden version of the method (and not the original version defined in the superclass) is the version that is actually executed.

Although not particularly obvious in this example, this is one of the important characteristics of runtime polymorphism.  When a method is invoked on a reference to an object, it is the type of the object (and not the type of the variable containing the reference to the object) that is used to determine which version of the method is actually invoked.

Three other instance methods

The subclass named Combo defines three other instance methods:

The code in these three methods is identical to the code in the methods having the same name in the program named Radio02 in the previous lesson.  I discussed that code in the previous lesson and won't repeat that discussion here.  You can view those methods in the complete listing of the program shown in Listing 5 near the end of this lesson.

The driver class

Listing 4 shows the code for the driver class named Radio03.
 
public class Radio03{
  //This class simulates the 
  // manufacturer and the human user
  public static void main(
                        String[] args){
    Combo myObjRef = new Combo();
    myObjRef.setStationNumber(3,93.5);
    myObjRef.playStation(3);
    myObjRef.insertTape();
    myObjRef.playStation(3);
    myObjRef.removeTape();
    myObjRef.playStation(3);
    myObjRef.playTape();
    myObjRef.insertTape();
    myObjRef.playTape();
    myObjRef.removeTape();
    myObjRef.playStation(3);
  }//end main
}//end class Radio03

Listing 4

The code in Listing 4 is also identical to the code in the program named Radio02 discussed in the previous lesson.  Therefore, I won't discuss it in detail here.

A new object of the Combo class

I present this code here solely to emphasize that this code instantiates a new object of the Combo class.  This assures that the overridden version of the method named playStation will be executed by the boldface statements in Listing 4 that invoke the playStation method.

(Although it is not the case in Listing 4, even if the reference to the object of type Combo had been stored in a reference variable of type Radio, instead of a reference variable of type Combo, invocation of the playStation method on that reference would have caused the overridden version of the method to have been executed.  That is the essence of runtime polymorphism based on overridden methods in Java.)
Program output

This program produces the following output on the computer screen:

Combo object constructed
Play Radio
  Playing the station at 93.5 Mhz
Insert Tape
  Tape is in
  Radio is off
Play Radio
  Remove the tape first
Remove Tape
  Tape is out
  Radio is on
Play Radio
  Playing the station at 93.5 Mhz
Play Tape
  Insert the tape first
Insert Tape
  Tape is in
  Radio is off
Play Tape
  Tape is playing
Remove Tape
  Tape is out
  Radio is on
Play Radio
  Playing the station at 93.5 Mhz

I will leave it as an exercise for the student to compare this output with the messages sent to the object by the code in Listing 4.

Summary

An object instantiated from a class that extends another class will contain all of the methods and all of the variables defined in the subclass, plus all of the methods and all of the variables inherited into the subclass.

The behavior of methods inherited into the subclass may not be appropriate for an object instantiated from the subclass.  You can change that behavior by overriding the method in the definition of the subclass.

To override a method in the subclass, reproduce the name, argument list, and return type of the original method in a new method definition in the subclass.  Make sure that the overridden method is not less accessible than the original method.  Also, make sure that it doesn't throw any checked exceptions that were not declared for the original method.

Provide a body for the overridden method, causing the behavior of the overridden method to be appropriate for an object of the subclass.

Any method that is not declared final can be overridden in a subclass.

The program discussed in this lesson extends a Radio class to produce a subclass that simulates an upgraded car radio containing a tape player.  Method overriding is used to modify the behavior of an inherited method named playStation to cause that method to behave appropriately when a tape has been inserted into the radio.

Method overriding is different from method overloading.  Method overloading will be discussed in the next lesson.

What's Next?

In the next lesson, I will explain the use of overloaded methods for the purpose of achieving compile-time polymorphism.

Complete Program Listing

A complete listing of the program is shown in Listing 5 below.
 
Copyright 2002, R.G.Baldwin
Simulates the manufacture and use of a 
combination car radio and tape player.
Uses method overriding to avoid 
modifying the class named Radio.

This program produces the following
output on the computer screen:
  
Combo object constructed
Play Radio
  Playing the station at 93.5 Mhz
Insert Tape
  Tape is in
  Radio is off
Play Radio
  Remove the tape first
Remove Tape
  Tape is out
  Radio is on
Play Radio
  Playing the station at 93.5 Mhz
Play Tape
  Insert the tape first
Insert Tape
  Tape is in
  Radio is off
Play Tape
  Tape is playing
Remove Tape
  Tape is out
  Radio is on
Play Radio
  Playing the station at 93.5 Mhz
**************************************/

public class Radio03{
  //This class simulates the 
  // manufacturer and the human user
  public static void main(
                        String[] args){
    Combo myObjRef = new Combo();
    myObjRef.setStationNumber(3,93.5);
    myObjRef.playStation(3);
    myObjRef.insertTape();
    myObjRef.playStation(3);
    myObjRef.removeTape();
    myObjRef.playStation(3);
    myObjRef.playTape();
    myObjRef.insertTape();
    myObjRef.playTape();
    myObjRef.removeTape();
    myObjRef.playStation(3);
  }//end main
}//end class Radio03
//===================================//

class Radio{
  //This class simulates the plans from
  // which the radio object is created.
  // This code is the same as in the
  // program named Radio01.
  protected double[] stationNumber = 
                         new double[5];
                            
  public void setStationNumber(
                int index,double freq){
    stationNumber[index] = freq;
  }//end method setStationNumber
  
  //This version of playStation doesn't
  // accommodate tape operations.
  public void playStation(int index){
    System.out.println(
            "Playing the station at " 
               + stationNumber[index]
               + " Mhz");
  }//end method playStation
  
}//end class Radio
//===================================//

class Combo extends Radio{
  
  private boolean tapeIn = false;
  //---------------------------------//
  
  public Combo(){//constructor
    System.out.println(
           "Combo object constructed");
  }//end constructor
  //---------------------------------//
  
  //Overridden playStation method. This
  // overridden version accommodates
  // tape operations.
  public void playStation(int index){
    System.out.println("Play Radio");
    if(!tapeIn){
      System.out.println(
          "  Playing the station at " 
               + stationNumber[index]
               + " Mhz");
    }else{
      System.out.println(
            "  Remove the tape first");
    }//end if/else
  }//end method playStation
  //---------------------------------//
  
  public void insertTape(){
    System.out.println("Insert Tape");
    tapeIn = true;
    System.out.println(
                       "  Tape is in");
    System.out.println(
                     "  Radio is off");
  }//end insertTape method
  //---------------------------------//
  
  public void removeTape(){
    System.out.println("Remove Tape");
    tapeIn = false;
    System.out.println(
                      "  Tape is out");
    System.out.println(
                      "  Radio is on");
  }//end removeTape method
  //---------------------------------//
  
  public void playTape(){
    System.out.println("Play Tape");
    if(!tapeIn){
      System.out.println(
            "  Insert the tape first");
    }else{
      System.out.println(
                  "  Tape is playing");
    }//end if/else
  }//end playTape
}//end class combo

Listing 5


Copyright 2001, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, TX) and private consultant whose primary focus is a combination of Java and XML. In addition to the many platform-independent benefits of Java applications, he believes that a combination of Java and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two.  He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Java Programming Tutorials, which has gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

baldwin.richard@iname.com

-end-