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:
- insertTape
- removeTape
- playTape
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 |
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.