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

Callbacks - II

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

Preface

This is the second 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.

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.

Sample Program

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

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.

Interesting Code Fragments

First we define an interface named CallBack that will create a new type and that declares 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 define a class whose objects can maintain a list of registered objects (registration is the process of placing an object on the list) of type CallBack and can notify all the objects on that list when something interesting happens. This class differs form the one in the previous version of the program in that it has the ability to notify for two different types of callbacks, recess() and lunch(). The name of this class is Teacher.

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()
That concludes the discussion of the class named Teacher. This is followed by three class definitions that implement the CallBack interface: Student, Dog, and Cat. These class definitions differ from the ones in the previous version of the program in that they define two callback methods instead of just one: recess() and lunch().

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
That brings us to the controlling class that ties all the pieces together and exercises them. Except for the fact that the main() method triggers two callbacks instead of just one, and the code to remove an object from the list was deleted for brevity, this code is essentially the same as in the previous program. Therefore, I am going to delete some of the redundant code from this fragment and primarily show only the new code.

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
The output from running this program follows. Note that the objects of the Student class respond to both the recess and the lunch callbacks. However, the objects of the Cat class respond only to the recess callbacks, and the objects of the Dog class respond only to the lunch callbacks.
 
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

Summary

In summary then, we have objects of different classes registered on a common callback list where every object on the list receives a callback for every different type of callback event associated with the list.

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.

Program Listing

A full listing of the program follows so that you can view the code fragments in context.
 
/*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
//=======================================================//
-end-