Students in Prof. Baldwin's Intermediate Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson.
Basically, this is a mechanism where a method in one object asks a method in another object to "call me back" or "notify me" when an interesting event happens.
For example, an interesting event might be that the price of a specified stock goes above its previous high value, or the toaster finishes toasting the bread.
A previous lesson introduced you to the basic Java callback mechanism using interfaces, and walked you through the development of a set of classes that implement a simple multicast form of callback. In that lesson, the definition of the CallBack interface was limited to a single method declaration.
In a real program involving callbacks, many different objects may ask one object to notify them when any interesting event in a family of interesting events happens, and to identify the specific event that actually happened along with the notification.
This lesson will enhance our previous program to accommodate this possibility.
As mentioned in the earlier lesson, it is usually easier to understand abstract concepts if they are explained in terms of a meaningful scenario. For that reason, we have conjured up a scenario in which to develop and explain our callback programs.
Our scenario consists of a teacher and some students. In the beginning there was only one student. Then we expanded the scenario to include many students and some animals in the classroom as well.
The students (and the animals) register themselves on the teachers rollbook to be notified of interesting events. Initially the interesting event was simply the teacher taking the roll. In this lesson, we expand that scenario to include notification that it is either time for recess, or it is time for lunch.
Initially, only one student received notification of one type of event. In this lesson, all of the students and all of the animals receive notification of both types of event (recess or lunch) but some of those who are notified choose to ignore the notification.
Without further discussion, let's look at some code.
The previous version of the program defined two different classes that implemented the CallBack interface. In order to give us more to work with, this version defines three different classes named Student, Dog, and Cat that implement the CallBack interface. Mixed objects of those three types are registered and maintained on a list and notified at callback time.
As before, this program defines a CallBack interface. However, this version of the interface declares two different methods that can be invoked as callback methods instead of just one.
In other words, in this case, the objects register to be notified whenever an interesting event from a family of interesting events occurs (the family has two members). When notified, the objects also need to be advised as to which interesting event actually happened.
The methodology for differentiating between the two different kinds of interesting events is to invoke one callback method in the case of one event, and to invoke the other callback method in the case of the other event.
All classes that implement the CallBack interface must define both methods, but it is allowable to define the method as an empty method. The net effect of defining a callback method as an empty method is to simply ignore the callback associated with that method. Note however, there there is some overhead associated with the invocation of an empty method.
(Although we haven't discussed the topic yet, I believe that this overhead is the reason that JavaSoft chose to separate the MouseListener and MouseMotionListener interfaces. Perhaps someone will remember to remind me to discuss that when we get to the topic of MouseMotionListener interface in the classroom lecture.)
One of the callback methods in this version of our program is named recess() and the other callback method is named lunch(). Thus, registered objects can be notified either of a recess() event, or of a lunch() event.
The Dog class ignores the recess() callback by defining an empty recess() method, and the Cat class ignores the lunch() callback by defining an empty lunch() method. The Student class responds to both types of callbacks with fully- defined methods.
The program defines a Teacher class that has the ability to create and maintain a list of objects of the interface type, and to notify those objects that something interesting has happened by invoking either the recess() method or the lunch() method on all the objects on the list.
It is important to note that every object on the list will be notified of both types of callbacks, although as mentioned above, a particular type of callback can be ignored by a class simply by leaving the callback method empty when it is defined.
As before, objects can be added to the list and then removed from the list. However, removal of objects from the list was demonstrated in the previous program, so removal is not demonstrated in this program.
Notification takes the form of invoking either the recess() method or the lunch() method on all the objects on the list.
Finally, the program defines a controlling class that ties all the pieces together and exercises them.
The program was tested using JDK 1.1.3 under Win95.
The output from the program is shown later.
interface CallBack{ public void recess(); public void lunch(); }//end interface CallBack |
The code to construct the Teacher object, add objects to the list, and remove objects from the list hasn't changed in a significant way, so we are going to skip over that code and go straight to the code that is new and different.
Basically what we now have is two different methods in place of one. One of the methods is named callRecess() and the other is named callLunch(). Except for their names, these methods are almost identical to the single method named callTheRoll() in the previous version of the program, so a lot of discussion isn't needed.
The code in the method makes a copy of the list and then uses a for loop along with some Vector methods to access each object reference. Then the callback method is invoked on each object reference.
We will show one of the methods below for reference.
void callRecess(){ Vector tempList;//save a temporary copy of list here //Make a copy of the list. synchronized(this){ tempList = (Vector)objList.clone(); }//end synchronized block //Invoke the recess() method on each object on // the list. for(int cnt = 0; cnt < tempList.size(); cnt++){ ((CallBack)tempList.elementAt(cnt)).recess(); }//end for loop }//end callRecess() |
Recall that I said that a class can ignore a particular type of callback simply by defining the callback method as an empty method. Recall also that I said that the Dog class ignores the recess() callback in just this way.
Because of the similarity of these three classes, I am only going to
show one of them below. I will show the Dog class to illustrate
how it defines an empty method to ignore the recess() callback.
class Dog implements CallBack{ String name; //store name here for later ID //-----------------------------------------------------// Dog(String name){//constructor this.name = name; //save the name to identify the obj }//end constructor //-----------------------------------------------------// public void recess(){ //ignore this callback with an empty method }//end recess() //-----------------------------------------------------// public void lunch(){//announce lunch System.out.println(name + " lunch"); }//end overridden lunch() }//end class Dog |
The code in the main() method of the controlling class instantiates a Teacher object named missJones, and then instantiates some objects of the three types: Student, Dog, and Cat. These objects are registered for callback by invoking the register() method on the Teacher object.
Then the code triggers a recess() callback and a lunch() callback.
The output produced by this program is shown following the listing of
this code fragment.
class Callback03{ public static void main(String[] args){ //Instantiate Teacher object Teacher missJones = new Teacher(); //Instantiate some Student objects //... code deleted //Instantiate some Dog objects. //... code deleted //Instantiate some Cat objects //... code deleted //Register some Student, Dog, and Cat objects with // the Teacher object. missJones.register(tom); missJones.register(spot); missJones.register(sue); missJones.register(cleo); missJones.register(fido); missJones.register(peg); missJones.register(kitty); missJones.register(bob); missJones.register(brownie); //Cause the Teacher object to call recess and lunch // on all the objects on the list. missJones.callRecess(); missJones.callLunch(); }//end main() }//end class Callback03 |
Tom recess Sue recess CleoCat recess Peg recess KittyKat recess Bob recess Tom lunch SpotDog lunch Sue lunch FidoDog lunch Peg lunch Bob lunch BrownieDog lunch |
The different types of callbacks are established by the method declarations in the CallBack interface.
Each class of object that registers for callbacks can either respond to all of the different types of callbacks by providing full definitions for all of the callback methods, or can selectively ignore some types of callbacks by defining those callback methods as empty methods.
/*File Callback03.java Copyright 1997, R.G.Baldwin The purpose of this program is to develop a callback capability using Interfaces. This is an enhanced version of the program named Callback02. You should familiarize yourself with the earlier program before getting into this program. This version defines three different classes named Student, Dog, and Cat that implement the CallBack interface. Mixed objects of those three types are maintained on a list and notified at CallBack time. Tested using JDK 1.1.3 under Win95. The output from the program was: Tom recess Sue recess CleoCat recess Peg recess KittyKat recess Bob recess Tom lunch SpotDog lunch Sue lunch FidoDog lunch Peg lunch Bob lunch BrownieDog lunch **********************************************************/ import java.util.*; //First we define an interface that will create a new type // and declare two generic methods that can be used to // callback any object that is of a class that implements // the interface. interface CallBack{ public void recess(); public void lunch(); }//end interface CallBack //=======================================================// //Next we need a class whose objects can maintain a // registered list of objects of type CallBack and can // notify all the objects on that list when something // interesting happens. This class has the ability to // notify of two different types of callbacks, recess() // and lunch(). class Teacher{ Vector objList; //list of objects of type CallBack //-----------------------------------------------------// Teacher(){//constructor //Instantiate a Vector object to contain the list // of registered objects. objList = new Vector(); }//end constructor //-----------------------------------------------------// //Method to add objects to the list. synchronized void register(CallBack obj){ this.objList.addElement(obj); }//end register() //-----------------------------------------------------// //Method to remove objects from the list. synchronized void unRegister(CallBack obj){ if(this.objList.removeElement(obj)) System.out.println(obj + " removed"); else System.out.println(obj + " not in the list"); }//end register() //-----------------------------------------------------// //Method to notify all objects on the list that // something interesting has happened regarding recess. void callRecess(){ Vector tempList;//save a temporary copy of list here //Make a copy of the list. synchronized(this){ tempList = (Vector)objList.clone(); }//end synchronized block //Invoke the recess() method on each object on // the list. for(int cnt = 0; cnt < tempList.size(); cnt++){ ((CallBack)tempList.elementAt(cnt)).recess(); }//end for loop }//end callRecess() //-----------------------------------------------------// //Method to notify all objects on the list that // something interesting has happened regarding lunch. void callLunch(){ Vector tempList;//save a temporary copy of list here //Make a copy of the list. synchronized(this){ tempList = (Vector)objList.clone(); }//end synchronized block //Invoke the lunch() method on each object on // the list. for(int cnt = 0; cnt < tempList.size(); cnt++){ ((CallBack)tempList.elementAt(cnt)).lunch(); }//end for loop }//end callRecess() //-----------------------------------------------------// }//end class Teacher //=======================================================// //Class that implements the CallBack interface. Objects // of this class can be registered on the list maintained // by an object of the Teacher class, and will be notified // whenever that object invokes either the recess() method // or the lunch() method on the registered objects on // the list. This method provides a full definition for // both methods. class Student implements CallBack{ String name; //store the object name here for later ID //-----------------------------------------------------// Student(String name){//constructor this.name = name; //save the name to identify the obj }//end constructor //-----------------------------------------------------// //An object of the Teacher class can invoke this method // as a callback mechanism. public void recess(){//announce recess System.out.println(name + " recess"); }//end overridden recess() //-----------------------------------------------------// //An object of the Teacher class can also invoke this // method as a callback mechanism. public void lunch(){//announce recess System.out.println(name + " lunch"); }//end overridden lunch() }//end class Student //=======================================================// //Another Class that implements the CallBack interface. // See description above. This class defines the recess() // method as an empty method. class Dog implements CallBack{ String name; //store name here for later ID //-----------------------------------------------------// Dog(String name){//constructor this.name = name; //save the name to identify the obj }//end constructor //-----------------------------------------------------// //An object of the Teacher class can invoke this method // as the callback mechanism. public void recess(){//announce recess //ignore this callback with an empty method }//end overridden recess() //-----------------------------------------------------// //An object of the Teacher class can also invoke this // method as a callback mechanism. public void lunch(){//announce recess System.out.println(name + " lunch"); }//end overridden lunch() }//end class Dog //=======================================================// //A third Class that implements the CallBack interface, // similar to the other two classes. This class defines // the lunch() method as an empty method. class Cat implements CallBack{ String name; //store name here for later ID //-----------------------------------------------------// Cat(String name){//constructor this.name = name; //save the name to identify the obj }//end constructor //-----------------------------------------------------// //An object of the Teacher class can invoke this method // as the callback mechanism. public void recess(){//announce recess System.out.println(name + " recess"); }//end overridden recess() //-----------------------------------------------------// //An object of the Teacher class can also invoke this // method as a callback mechanism. public void lunch(){//announce recess //ignore this callback with an empty method }//end overridden lunch() }//end class Cat //=======================================================// //Controlling class that ties all the pieces together and // exercises them. class Callback03{ public static void main(String[] args){ //Instantiate Teacher object Teacher missJones = new Teacher(); //Instantiate some Student objects Student tom = new Student("Tom"); Student sue = new Student("Sue"); Student peg = new Student("Peg"); Student bob = new Student("Bob"); Student joe = new Student("Joe"); //Instantiate some Dog objects. Dog spot = new Dog("SpotDog"); Dog fido = new Dog("FidoDog"); Dog brownie = new Dog("BrownieDog"); //Instantiate some Cat objects Cat cleo = new Cat("CleoCat"); Cat kitty = new Cat("KittyKat"); //Register some Student, Dog, and Cat objects with // the Teacher object. missJones.register(tom); missJones.register(spot); missJones.register(sue); missJones.register(cleo); missJones.register(fido); missJones.register(peg); missJones.register(kitty); missJones.register(bob); missJones.register(brownie); //Cause the Teacher object to call recess on all // the objects on the list. missJones.callRecess(); //Cause the Teacher object to call lunch on all // the objects on the list. missJones.callLunch(); }//end main() }//end class Callback03 //=======================================================// |