Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

Callbacks - I

Java Programming, Lecture Notes # 77, Revised 01/04/98.

Preface

This is the first in a series of three consecutive lessons on Callbacks in Java. The three lessons are named Callbacks - I, Callbacks - II, and Callbacks - III.

Students in Prof. Baldwin's Intermediate Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson.

Introduction

Many processes in the standard Java API make use of a mechanism that in other programming environments might be referred to as a callback mechanism.

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.

In fact, many different objects may ask one object to notify them when the interesting event happens. This is sometimes referred to as multicasting. (The one-to-one case is often referred to as unicasting.)

Going further, 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.

For example, we see different forms of callback activity in conjunction with the Delegation Event Model used with GUIs in JDK 1.1, the Observer/Observable concept used in the Model-View-Controller paradigm, the concept of Bound Properties and Constrained Properties in Java Beans, etc. You can find examples of all of these in the pages of these tutorial lessons.

Callback capabilities are often implemented in other languages by passing a function pointer to another function. The receiving function uses the passed function pointer to invoke another function when an interesting event happens. Java doesn't support pointers. In this lesson, we will learn how to implement the callback mechanism using interfaces instead.

As usual, our approach will be to learn the material by reviewing programs that progress from very simple to more complex. This topic will consume all of this lesson and the next two lessons as well.

It is usually easier to understand abstract concepts if they are explained in terms of a meaningful scenario. In this case, our scenario will consist of a teacher and some students. In the beginning there will only be one student. Ultimately there will be many students and there will also be 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 will simply be the teacher taking the roll. Ultimately the interesting event will be notification that it is either time for recess, or it is time for lunch.

Initially, only one student receives notification of the one type of event. Ultimately, 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.

We will refer to the case where only one student is on the list as the unicast program. We will refer to the case where many students (and possibly animals as well) are on the list as the multicast program. This terminology was selected because it matches the terminology used in the JDK 1.1 documentation for the Delegation Event Model.

Without further discussion, let's look at some code.

Unicast Sample Program

The purpose of this program is to develop a callback capability using Interfaces. This version of the program is designed to emphasize the structure of the process. Therefore an effort was made to avoid the requirement for any extra code so it doesn't do anything fancy.

This program defines a CallBack interface (interface named CallBack) that can be used to establish a new type of object reference, and also to declare the interface to a method named callBack() that will be contained in all objects of classes that implement the interface. This method will then be used to notify those objects whenever something interesting happens.

The program defines a Teacher class that has the ability to

As mentioned earlier, the size of the list was constrained to only one object in order to emphasize callback structure and avoid getting bogged down in list processing. A subsequent version will implement list processing.

The program defines a class named Student that implements the CallBack interface. Objects of the Student class can be registered on the list maintained by an object of the Teacher class, and can be notified by the object of the Teacher class whenever something interesting happens. Notification takes the form of invoking the callBack() method on the object.

The body of the callBack() method can be designed to do anything, but in this case, to keep things simple, it just announces that it has been called.

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 in the full program listing in a later section.

Unicast Interesting Code Fragments

First we define an interface named CallBack that will create a new type and declare a generic method named callBack() that can be used to execute a callback on any object that is instantiated from a class that implements the interface.
 
interface CallBack{
  public void callBack();
}//end interface CallBack
Next we need a class whose objects can maintain a list of objects of type CallBack (objects whose class implements the CallBack interface).

We refer to the process of putting an object on the list is registering the object.

This class also needs to have the ability to notify all the objects on that list when something interesting happens. We will name this class Teacher in keeping with the scenario described earlier.

As mentioned earlier, to keep things simple, and emphasize the callback structure without getting bogged down in list processing, we will begin with a limitation of one object for the length of the list.

This is a simple class consisting of one instance variable of type CallBack (the interface type) and two instance methods.

One of the methods named register() places an object on the list.

The other method named callTheRoll() invokes the callBack() method on the object that is on the list.

Note that the object on the list is guaranteed to have a method named callBack(). Otherwise, it couldn't get on the list in the first place. This is because the register() method requires the object to be of type CallBack.
 
class Teacher{
  CallBack obj; //list of objects of type CallBack
  //-----------------------------------------------------//
  
  //Method to add objects to the list.
  void register(CallBack obj){
    this.obj = obj;
  }//end register()
  //-----------------------------------------------------//
  
  //Method to notify all objects on the list
  void callTheRoll(){
    obj.callBack();
  }//end callTheRoll()
  //-----------------------------------------------------//
}//end class Teacher
Next, we need a 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 the callBack() method on the registered objects on the list. In keeping with the scenario described earlier, we will name this class Student.

By claiming to implement the CallBack interface, this class is required to provide a definition for the method named callBack() that is declared in the interface. Otherwise, the program won't compile. In this case, that definition is rather simple. The callBack() method simply announces that it has been called.

As we saw above, an object of the Teacher class will invoke the callBack() method on all objects on its list when the interesting event occurs. It is important to realize that the invocation of this method is the callback mechanism.
 
class Student implements CallBack{
  String name;
  //-----------------------------------------------------//
  
  Student(String name){//constructor
    this.name = name;  //save the name to identify the obj
  }//end constructor
  //-----------------------------------------------------//
 
  public void callBack(){
    System.out.println(name + " here");
  }//end callBack()
}//end class Student
Finally, we need a controlling class to tie all the pieces together and to exercise them. The main() method in this class

All in all, this is not too complicated once you break the process into its component parts.
 
class Callback01{
  public static void main(String[] args){
    //Instantiate Teacher object
    Teacher missJones = new Teacher();
    //Instantiate and register a Student object with the
    // Teacher object
    missJones.register(new Student("Joe"));
    //Cause the Teacher object to do a callBack on the
    // Student object.
    missJones.callTheRoll();
  }//end main()
}//end class Callback01
There you have it. This simple program contains the sum and substance of one approach to callbacks in Java.

It is critical to note that the objects registered on the list are of the interface type CallBack. This guarantees that there cannot be an object on the list that does not have an instance method named callBack().

Unicast Program Listing

A full listing of the program follows so that you can view the code fragments in context.
 
/*File Callback01.java Copyright 1997, R.G.Baldwin
The purpose of this program is to develop a callback
capability using Interfaces.  This version of the
program is designed to emphasize the structure of
the process, and therefore an effort was made to
avoid the requirement for any extra code to do
anything fancy.

Tested using JDK 1.1.3 under Win95.

The output from the program is:
  
Joe here.
**********************************************************/
//First we define an interface that will create a new type
// and declare a generic method that can be used to 
// callback any object that is of a class that implements
// the interface.
interface CallBack{
  public void callBack();
}//end interface CallBack
//=======================================================//

//Next we need a class whose objects can maintain a 
// registered list of objects of type CallBack (whose
// class implements the CallBack interface) and can 
// notify all the objects on that list when something
// interesting happens.

//To keep things simple, and emphasize the structure of
// what we are doing, we will begin with a limitation
// of one object on the length of the list.

class Teacher{
  CallBack obj; //list of objects of type CallBack
  //-----------------------------------------------------//
  
  //Method to add objects to the list.
  void register(CallBack obj){
    this.obj = obj;
  }//end register()
  //-----------------------------------------------------//
  
  //Method to notify all objects on the list that 
  // something interesting has happened.  
  void callTheRoll(){
    //Invoke the callBack() method on the object.  The
    // object is guaranteed to have such a method because
    // it is of a class that implements the CallBack
    // interface.
    obj.callBack();
  }//end callTheRoll()
  //-----------------------------------------------------//
}//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 the callBack method on the
// registered objects on the list.
class Student implements CallBack{
  String name;
  //-----------------------------------------------------//
  
  Student(String name){//constructor
    this.name = name;  //save the name to identify the obj
  }//end constructor
  //-----------------------------------------------------//

  //An object of the Teacher class will invoke this method
  // as the callback mechanism to notify an object of this
  // class that something interesting has happened.  
  public void callBack(){
    System.out.println(name + " here");
  }//end overridden callBack()
}//end class Student
//=======================================================//

//Controlling class that ties all the pieces together and
// exercises them.
class Callback01{
  public static void main(String[] args){
    //Instantiate Teacher object
    Teacher missJones = new Teacher();
    //Instantiate and register a Student object with the
    // Teacher object
    missJones.register(new Student("Joe"));
    //Cause the Teacher object to do a callBack on the
    // Student object.
    missJones.callTheRoll();
  }//end main()
}//end class Callback01
//=======================================================//
.

Multicast Sample Program

The multicast version of this program does not modify the basic callback mechanism developed in the previous program. It simply enhances that mechanism to make it possible to maintain a list of objects registered for callback and to notify all the objects on that list when an interesting event happens.

In case you started reading at this point, this is an enhanced version of the program named Callback01. You should familiarize yourself with that program before trying to understand this program.

This version has the capability to create and maintain a list of objects that register for callback whereas the program named Callback01 could only remember a single object for callback.

In addition, this version defines two different classes that implement the CallBack interface. Mixed objects of those two types are maintained on the list and notified at callback time. This is a subtle but very important point. It is not necessary that all the objects that are registered on a callback list be of the same type, only that they all be of a class that implements the CallBack interface.

As before, this program defines a CallBack interface that can be used to establish a new type of object, and also to declare the interface to a method named callBack() that is contained in all objects of classes that implement the interface. Because this method is guaranteed to be contained in all of the objects on the list, it can be used to notify those objects whenever something interesting happens.

The program defines a Teacher class that has the ability to create and maintain a list of objects of the interface type (CallBack), and to notify those objects that something interesting has happened by invoking the callBack() method on each of the objects on the list.

The size of the list is limited only to the largest Vector object that can be accommodated by the system. (We learned about Vector objects in an earlier lesson.)

The program defines a class named Student that implements the CallBack interface. The program also defines a class named Dog that implements the CallBack interface as well. (Back in the description of the scenario, I promised you that missJones was going to have to deal with animals in the classroom. I'm sure glad I don't have that problem.)

Objects of the Student and Dog classes can be registered on the list maintained by an object of the Teacher class, and can be notified by the object of the Teacher class whenever something interesting happens.

Note that objects can be added to the list and then removed from the list. One object is first added and later removed for demonstration purposes.

As before, notification takes the form of invoking the callBack() method on each of the objects on the list.

The body of the callBack() methods in the classes that implement the interface can be designed to do anything. In this case, to keep things simple, they just announce that they have been called. However, they make the announcement in slightly different ways.

This program contains display statements in the registration and notification methods for demonstration purposes only, and to allow us to track what is happening as the program runs.

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 following a discussion of the controlling class at the end of the next section.

Multicast Interesting Code Fragments

As before, we first define an interface that will create a new type and declare a generic method that can be used to callback any object that is of a class that implements the interface. There is nothing new here.
 
interface CallBack{
  public void callBack();
}//end interface CallBack
Next we need a class whose objects can maintain a registered list of objects of type CallBack (objects whose class implements the CallBack interface) and can notify all the objects on that list when something interesting happens. As before, we name this class Teacher.

This class has grown to the point that we will break it into parts and discuss them separately.

There is quite a bit here that is new, due simply to the requirement for list processing. There is nothing new about the basic callback mechanism.

We start out by replacing the single instance variable of type CallBack by a reference to an object of type Vector. We will maintain our list in this object. Recall that a Vector object can only work with references to objects of type Object, so this will entail some downcastng later.

The constructor for our new Teacher class instantiates the Vector object.
 
class Teacher{
  Vector objList; //list of objects of type CallBack
  //----------------------------------------------//
  
  Teacher(){//constructor
    objList = new Vector();
  }//end constructor
Next we need a method to add objects to the list. We will synchronize it to protect against the possibility of two or more objects on different threads trying to register at the same time.

Note that the references to the objects are received as type CallBack, which is the interface type, and stored as type Object, because the Vector class only accommodates references to objects of type Object. Again, this will lead to some downcasting requirements later.
 
  synchronized void register(CallBack obj){
    this.objList.addElement(obj);
    System.out.println(obj + " added");
  }//end register()
To be general, we also need a method to remove objects from the list. Removal of an object from the list is a little more complicated than adding an object to the list due to the possibility of having two or more identical objects on the list (we could, and possibly should, guard against that possibility when constructing the list).

The following partial excerpt from the JDK 1.1.3 documentation describes the removeElement() method of the Vector class that we are using to accomplish this (three different methods are available to remove objects from a Vector).
 
public final synchronized boolean removeElement(Object obj) 

This method removes the first occurrence of the argument from this vector. Indices beyond that point are adjusted appropriately 

Parameters: obj - the component to be removed. 

Returns: true if the argument was a component of this vector; false otherwise.

Given that explanation, the code for removal of an object from the list is straightforward.
 
  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()
Now we need a method to notify all of the objects on the list that something interesting has happened. We will name this method callTheRoll() to adhere to our classroom scenario.

One of the potential problems of this type of callback mechanism is that when the callback method is invoked on an object, that method might take a while to finish. (As an aside, when writing callback methods, if they do anything significant, the code in the method should probably spawn another thread to do the actual work and return as quickly as possible.)

This leads to the possibility that additional objects might attempt to register during that time interval. To protect against this, we make a copy of the state of the list object as it existed at the point in time that the decision was made to do the callbacks, and then perform the callbacks using that copy. That way, the original list is free to be updated as needed during this interval.

So, we start out by creating a clone of the list. We also synchronize this process to prevent the list from being modified while we are creating the clone.

Following this, we use a for loop to access all the objects on the list, and invoke the callBack() method on those objects. (Actually, the list contains references to objects, and not the actual objects, so we are invoking the method on the references.)

As promised earlier, we have to downcast from Object to CallBack to gain access to the callBack() method in the objects.
 
  void callTheRoll(){
    Vector tempList;//save a temporary copy of list here
    
    synchronized(this){
      tempList = (Vector)objList.clone();
    }//end synchronized block
    
    for(int cnt = 0; cnt < tempList.size(); cnt++){
      ((CallBack)tempList.elementAt(cnt)).callBack();
    }//end for loop
  }//end callTheRoll()
That ends the discussion of the class named Teacher and brings us to the class named Student that implements the CallBack interface. This class hasn't changed. As indicated earlier, this version of the program also has a class named Dog that implements the interface. These two classes are essentially the same.

Because of their similarity, and because they are essentially the same as in the previous program, we will simply show the class named Dog with no further discussion.
 
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 will invoke this method
  // as the callback mechanism to notify an object of this
  // class that something interesting has happened.  
  
  public void callBack(){//announce callBack

    System.out.println("Woof, Woof " + name);
  }//end overridden callBack()
}//end class Dog
That brings us to the controlling class that ties all the pieces together and exercises them. This version of the program differs from the previous version primarily in terms of the volume of Student and Dog objects to be instantiated and registered on the Teacher object. There are also a lot of display statements to help us keep track of what is going on.

The ability to remove objects from the list is also illustrated.

Finally, the callback to the objects on the list is executed by invoking the callTheRoll() method on the Teacher object named missJones. The output from running this program follows the listing of the code fragment.

A subtle, but extremely important point is illustrated here. Student and Dog are different classes. Objects of both of those classes are being registered on the single object of the Teacher class. The Teacher object doesn't care that they are different, so long as they are all instantiated from classes that implement the CallBack interface. The register() method will only accept object references of type CallBack.
 
class Callback02{
  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("Spot");
    Dog fido = new Dog("Fido");
    Dog brownie = new Dog("Brownie");

    //Register some Student and Dog objects with the 
    // Teacher object.
    System.out.println("Register Tom");
    missJones.register(tom);
    System.out.println("Register Spot");
    missJones.register(spot);
    System.out.println("Register Sue");
    missJones.register(sue);
    System.out.println("Register Fido");
    missJones.register(fido);
    System.out.println("Register Peg");
    missJones.register(peg);
    System.out.println("Register Bob");
    missJones.register(bob);
    System.out.println("Register Brownie");
    missJones.register(brownie);
    
    //Remove a Student object from the list.
    System.out.println("Remove Peg");
    missJones.unRegister(peg);
    
    //Try to remove an object that is not on the list.
    System.out.println("Try to remove Joe");
    missJones.unRegister(joe);
    
    System.out.println();//blank line
    
    //Cause the Teacher object to do a callBack on all
    // the objects on the list.
    missJones.callTheRoll();
  }//end main()
}//end class Callback02
The output from running this program follows. You can see the identification of each individual object as it is added to, or removed from the list.

Note that the attempt to remove Joe from the list was not successful because he was never registered in the first place.

Finally, you see the output produced by invoking callTheRoll() which in turn invokes the callBack() method on each of the objects on the list.

Note that Peg didn't appear in the rollcall because she was first added and then removed from the list before the rollcall was taken..
 
Register Tom
Student@1cc73e added
Register Spot
Dog@1cc74e added
Register Sue
Student@1cc741 added
Register Fido
Dog@1cc751 added
Register Peg
Student@1cc744 added
Register Bob
Student@1cc747 added
Register Brownie
Dog@1cc754 added
Remove Peg
Student@1cc744 removed
Try to remove Joe
Student@1cc74a not in the list

Tom here
Woof, Woof Spot
Sue here
Woof, Woof Fido
Bob here
Woof, Woof Brownie
So there you have it, the sum and substance of multicast callbacks in Java. Obviously improvements could be made. We will see a couple of them in the next two lessons..

Multicast Program Listing

A complete listing of the multicast program follows so that you can view the code fragments in context.
 
/*File Callback02.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 
Callback01. You should familiarize yourself with
the earlier program before getting into this program.

This version has the added capability to create and 
maintain a list of objects that register for callback
whereas the program named Callback01 could only remember
a single object for callback.

Tested using JDK 1.1.3 under Win95.

The output from the program was:

Register Tom
Student@1cc73e added
Register Spot
Dog@1cc74e added
Register Sue
Student@1cc741 added
Register Fido
Dog@1cc751 added
Register Peg
Student@1cc744 added
Register Bob
Student@1cc747 added
Register Brownie
Dog@1cc754 added
Remove Peg
Student@1cc744 removed
Try to remove Joe
Student@1cc74a not in the list

Tom here
Woof, Woof Spot
Sue here
Woof, Woof Fido
Bob here
Woof, Woof Brownie
  
Note that Peg didn't appear in the callBack list because
she was first added to, and later removed from the list.
**********************************************************/
import java.util.*;

//First we define an interface that will create a new type
// and declare a generic method that can be used to 
// callback any object that is of a class that implements
// the interface.
interface CallBack{
  public void callBack();
}//end interface CallBack
//=======================================================//

//Next we need a class whose objects can maintain a 
// registered list of objects of type CallBack (whose
// class implements the CallBack interface) and can 
// notify all the objects on that list when something
// interesting happens.

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.  Synchronize to
  // protect against two or more objects on different
  // threads trying to register at the same time.  Note 
  // that the objects are received as type CallBack which
  // is the interface type, and stored as type Object,
  // because the Vector class only accommodates objects of
  // type Object.
  synchronized void register(CallBack obj){
    this.objList.addElement(obj);
    System.out.println(obj + " added");
  }//end register()
  //-----------------------------------------------------//
  
  //Method to remove objects from the list.
  synchronized void unRegister(CallBack obj){
    if(this.objList.removeElement(obj))
      //true when successfully found and removed
      System.out.println(obj + " removed");
    else//false on failure to find and remove
      System.out.println(obj + " not in the list");
  }//end register()
  //-----------------------------------------------------//
  
  //Method to notify all objects on the list that 
  // something interesting has happened.  
  void callTheRoll(){
    Vector tempList;//save a temporary copy of list here
    
    //Make a copy of the list to avoid the possibility of
    // the list changing while objects are being notified.
    // Synchronize to protect against list changing while
    // making the copy.
    synchronized(this){
      tempList = (Vector)objList.clone();
    }//end synchronized block
    
    //Invoke the callBack() method on each object on
    // the list.  The object are guaranteed to have such
    // a method, even if they are of different types,
    // because they are all of a class that implements 
    // the CallBack interface. If not, they could not
    // have been registered on the list in the first
    // place.  Note the requirement to downcast to
    // type CallBack.
    for(int cnt = 0; cnt < tempList.size(); cnt++){
      ((CallBack)tempList.elementAt(cnt)).callBack();
    }//end for loop
  }//end callTheRoll()
  //-----------------------------------------------------//
}//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 the callBack method on the
// registered objects on the list.  This program will not
// compile if this class fails to implement the CallBack
// interface

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 will invoke this method
  // as the callback mechanism to notify an object of this
  // class that something interesting has happened.
  
  public void callBack(){//announce callBack
    System.out.println(name + " here");
  }//end overridden callBack()
}//end class Student
//=======================================================//

//Another Class that implements the CallBack interface.  
// Objects of this class can also be registered on the list
// maintained by an object of the Teacher class, and will
// also be notified whenever that object invokes the 
// callBack() method on the registered objects on the 
// list. This program will not compile if this class
// fails to implement the CallBack interface.

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 will invoke this method
  // as the callback mechanism to notify an object of this
  // class that something interesting has happened.  
  
  public void callBack(){//announce callBack
    System.out.println("Woof, Woof " + name);
  }//end overridden callBack()
}//end class Dog
//=======================================================//

//Controlling class that ties all the pieces together and
// exercises them.
class Callback02{
  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("Spot");
    Dog fido = new Dog("Fido");
    Dog brownie = new Dog("Brownie");

    //Register some Student and Dog objects with the 
    // Teacher object.
    System.out.println("Register Tom");
    missJones.register(tom);
    System.out.println("Register Spot");
    missJones.register(spot);
    System.out.println("Register Sue");
    missJones.register(sue);
    System.out.println("Register Fido");
    missJones.register(fido);
    System.out.println("Register Peg");
    missJones.register(peg);
    System.out.println("Register Bob");
    missJones.register(bob);
    System.out.println("Register Brownie");
    missJones.register(brownie);
    
    //Remove a Student object from the list.
    System.out.println("Remove Peg");
    missJones.unRegister(peg);
    
    //Try to remove an object that is not on the list.
    System.out.println("Try to remove Joe");
    missJones.unRegister(joe);
    
    System.out.println();//blank line
    
    //Cause the Teacher object to do a callBack on all
    // the objects on the list.
    missJones.callTheRoll();
  }//end main()
}//end class Callback02
//=======================================================//
-end-