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

Callbacks - III

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

Preface

This is the third 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

Hopefully, by this point, you are familiar with the callback material in the previous two lessons. Therefore, I will avoid, insofar as practical, repeating the introductory material from the previous lessons.

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.

In addition, it is often desirable to encapsulate information in an object and to pass that object as a parameter when the callback method is invoked. This lesson will enhance the previous program by providing this capability.

Also, sometimes the interfaces used to implement callbacks declare many callback methods, and some interested classes are interested in only a few of those methods. It can become burdensome to have to provide large numbers of empty methods in all the classes that implement such an interface. For this reason, a mechanism known in Java terms as an adapter class is used to reduce the burden.

This lesson will show you how to create and use such adapter classes.

We will continue with our earlier scenario consisting of a teacher and some students. At this point, we have expanded our scenario to include many students and some animals in the classroom.

The students (and the animals) register themselves on the teachers rollbook to be notified when it is time for recess, or it is time for lunch.

In this lesson, as in the previous 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. However, in this lesson, the methodology for ignoring the notification is different than it was in the previous lesson.

Sample Program

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

This version of the program makes two unrelated enhancements to the program named Callback03.

First, a new class is defined in conjunction with the CallBack interface that makes it possible for the Teacher class to encapsulate information into an object and pass that object as a parameter whenever a callback is made.

Second, a new class is defined in conjunction with the CallBack interface which, in Java terminology, is often called an adapter class.

An adapter class, as defined here, is a convenience class that implements the interface and defines all the methods declared in the interface with empty methods. Then any class that needs to implement the interface can extend the adapter class without a requirement to define those methods that are not of interest.

In this version of the program, the CallBAck interface is expanded to declare several dummy methods to emphasize this benefit of the use of an adapter class.

Note that objects of a class that extends the adapter class can be referred to by the interface type.

Thus, interface type is an inherited attribute that results from extending a class that implements an interface.

Using an adapter class also makes it possible to expand the interface later, by declaring new methods, without breaking code that already implements the interface, provided that the adapter class is appropriately redefined. In this sense, the adapter class is a little more than a convenience class.

This version of the program defines three different classes named Student, Dog, and Cat, that implement the CallBack interface indirectly by extending the adapter class.

Mixed objects of these three types are maintained in a common list and notified at callback time.

As in the previous version of the program, the Dog class ignores the recess() callback and the Cat class ignores the lunch() callback. In this program, these classes simply don't bother to override methods of the interface that are not of interest.

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. An object is instantiated and passed as a parameter whenever one or the other of these methods is invoked.

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.

Interesting Code Fragments

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.

Note that in addition to these two methods, the interface also declares several additional dummy methods to emphasize the benefit of extending the adapter class that is defined later.

Note also that the methods now require a parameter. The class for this parameter will be defined soon next.
 
interface CallBack{
  public void recess(CallBackObjectClass obj);
  public void lunch(CallBackObjectClass obj);
  
  public void dummy1(CallBackObjectClass obj);
  public void dummy2(CallBackObjectClass obj);
  public void dummy3(CallBackObjectClass obj);
  public void dummy4(CallBackObjectClass obj);
  public void dummy5(CallBackObjectClass obj);
  public void dummy6(CallBackObjectClass obj);

}//end interface CallBack
The following class definition is for a class from which we can instantiate an object to pass as a parameter in the callback methods. This class can be as simple or as elaborate as needed to convey the amount and type of information needed with the callback. In our case, it contains a single String object.
 
class CallBackObjectClass{
  String data;
  
  CallBackObjectClass(String data){//constructor
    this.data = data;
  }//end constructor
}//end CallBackObjectClass
The next code fragment is the definition of the adapter class for the CallBack interface. This class defines all the interface methods with empty methods.

Classes that need to implement the interface can now extend this class instead and override only the empty methods of interest, and ignore the others. It is important to note that the existence of the adapter does not prevent another class from implementing the interface instead of extending the adapter class.

Objects of a class that extends this class can be referenced as the interface type CallBack.
 
class CallBackAdapter implements CallBack{
  public void recess(CallBackObjectClass obj){};
  public void lunch(CallBackObjectClass obj){};
  
  public void dummy1(CallBackObjectClass obj){};
  public void dummy2(CallBackObjectClass obj){};
  public void dummy3(CallBackObjectClass obj){};
  public void dummy4(CallBackObjectClass obj){};
  public void dummy5(CallBackObjectClass obj){};
  public void dummy6(CallBackObjectClass obj){};  
}//end class CallBackAdapter
Next we need a class whose objects can maintain a list of registered objects of type CallBack and can notify all the objects on that list when something interesting happens. This class has the ability to invoke two different callback methods: recess() and lunch().

Obviously, if you have been following this development, you will know by now that this is the class named Teacher.

The only thing significantly different about this version of the Teacher class is the fact that it instantiates and passes an object of type CallBackObjectClass whenever it makes a callback.

Since you have seen this method several times before, and should be very familiar with it by now, I am going to delete most of the code in the class and show you only that portion of the code that invokes the recess() callback method on all the objects on the list, instantiating and passing an object of type CallBackObjectClass as a parameter in each case.
 
class Teacher{
  //code deleted

  void callRecess(){
    //code deleted

    for(int cnt = 0; cnt < tempList.size(); cnt++){
      ((CallBack)tempList.elementAt(cnt)).recess(
                      new CallBackObjectClass(" Recess"));
    }//end for loop
  }//end callRecess()
  //code deleted

}//end class Teacher
This brings us to the three classes that implement the CallBack interface indirectly by extending the CallBackAdapter class.

Objects of these three 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.

Part of the notification will be an object passed as a parameter to the callback method. Code in the callback method can either ignore that object, or can extract the information from the object and use it.

These three classes are very similar, so I will show you only one of them here. To be consistent with the previous lesson, I will show you the definition for the Dog class that ignores the recess() callback. This class also ignores all of the dummy methods that were declared in the CallBack interface in this program.

Note first that this class extends the CallBackAdapter class and does not implement any interfaces.

Note also that it only overrides the lunch() method from the adapter class, ignoring all of the other empty methods defined in that class. Were it not for the adapter class in this case, this Dog class definition would have to define the six dummy methods and the recess() method with empty methods. Therefore, the availability of such an adapter class can be a real convenience for the programmer.

Finally, note that code in the lunch() method extracts the instance variable named data and displays that information instead of a literal string as was the case in the previous versions of this program.
 
class Dog extends CallBackAdapter{
  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 lunch(CallBackObjectClass obj){//announce
    System.out.println(name + obj.data);
  }//end overridden lunch()
  //-----------------------------------------------------//
}//end class Dog
That brings us to the controlling class that ties all the other pieces together and exercises them. The controlling class in this version of the program is no different from that of the previous version named Callback03.

Also, the output from running this version of the program is almost the same as the previous version, the difference being that the callback methods display information encapsulated in an object passed to the method which was not the case in the previous versions. The output can be seen in the complete program listing that follows shortly.

Therefore, there should be no need to provide any further discussion regarding the testing of this version of the program..

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.

Whenever a callback occurs, an object is instantiated to contain information and passed as a parameter to the callback method. The code in the callback method can either ignore that object, or extract the information from the object and use it. In this program, the code in the callback methods extracts and displays the information encapsulated in the object.

The different types of callbacks are established by the method declarations in the CallBack interface.

An adapter class is defined that implements the CallBack interface and defines all of its methods as empty methods.

Each class of object that registers for callbacks can either respond to all of the different types of callbacks by overriding all of the callback methods in the adapter class, or can selectively ignore some types of callbacks simply by not overriding those methods.

Such a class can also ignore the adapter class and implement the interface directly as in the previous lessons.

Program Listing

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

This version of the program makes two unrelated 
enhancements to the program named Callback03.

First, a new class is defined in conjunction with the
CallBack interface that makes it possible for the
Teacher class to encapsulate information into an object
and pass that object whenever a callback is made.

Second, a new class is defined in conjunction with the
CallBack interface which, in Java terminology, is often
called an adapter class.  This is a convenience class
that implements the interface and defines all the 
methods declared in the interface with empty methods.
Then any class that needs to implement the interface 
can extend the adapter class without a requirement to 
define those methods that are not of interest.

In this case, the interface is expanded to declare 
several dummy methods to emphasize this benefit of
the use of an adapter class.

Note that objects of classes that extend the adapter
class that implements the interface can be referred to 
by the interface type. Thus, interface type is an
inherited attribute that results from extending a class.

Using an adapter class also makes it possible to expand
the interface later by declaring new methods without 
breaking code that already implements the interface.

This version defines three different classes named 
Student, Dog, and Cat, that implement the CallBack 
interface indirectly by extending the adapter class.
These classes simply don't bother to define
methods of the interface that are not of interest.

Mixed objects of these three types are maintained 
on a list and notified at CallBack time.

The Dog class ignores the recess() callback and the 
Cat class ignores the lunch() callback.  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.

Finally, the program defines a controlling class that
ties all the pieces together and exercises them.

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. Note that the methods now require a
// parameter. Note also that we have declared several
// additional dummy methods to emphasize the benefit of 
// extending the adapter class that is defined later.
interface CallBack{
  public void recess(CallBackObjectClass obj);
  public void lunch(CallBackObjectClass obj);
  
  public void dummy1(CallBackObjectClass obj);
  public void dummy2(CallBackObjectClass obj);
  public void dummy3(CallBackObjectClass obj);
  public void dummy4(CallBackObjectClass obj);
  public void dummy5(CallBackObjectClass obj);
  public void dummy6(CallBackObjectClass obj);

}//end interface CallBack
//=======================================================//

//Now we need a class that can be instantiated to
// pass an object in the callback methods.
class CallBackObjectClass{
  String data;
  
  CallBackObjectClass(String data){//constructor
    this.data = data;
  }//end constructor
}//end CallBackObjectClass
//=======================================================//

//Now we need an adapter class for the CallBack interface.
// This class defines all the interface methods with
// empty methods.  Classes that need the interface can
// now extend this class and override only the empty 
// methods of interest, and ignore the others. Note that 
// objects of a class that extends this class can be 
// referenced as the interface type CallBack.
class CallBackAdapter implements CallBack{
  public void recess(CallBackObjectClass obj){};
  public void lunch(CallBackObjectClass obj){};
  
  public void dummy1(CallBackObjectClass obj){};
  public void dummy2(CallBackObjectClass obj){};
  public void dummy3(CallBackObjectClass obj){};
  public void dummy4(CallBackObjectClass obj){};
  public void dummy5(CallBackObjectClass obj){};
  public void dummy6(CallBackObjectClass obj){};  
}//end class CallBackAdapter
//=======================================================//

//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 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 and to pass information to the object
  // encapsulated in an object as a parameter.
  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, passing an object as a parameter.
    for(int cnt = 0; cnt < tempList.size(); cnt++){
      ((CallBack)tempList.elementAt(cnt)).recess(
                      new CallBackObjectClass(" Recess"));
    }//end for loop
  }//end callRecess()
  //-----------------------------------------------------//
  //Method to notify all objects on the list that 
  // something interesting has happened regarding 
  // lunch and to pass an object containing information
  // as a parameter to the callback method.
  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, passing an object as a parameter.
    for(int cnt = 0; cnt < tempList.size(); cnt++){
      ((CallBack)tempList.elementAt(cnt)).lunch(
                       new CallBackObjectClass(" Lunch"));
    }//end for loop
  }//end callRecess()
  //-----------------------------------------------------//
}//end class Teacher
//=======================================================//

//Class that implements the CallBack interface indirectly
// by extending the CallBackAdapter class.  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 extends CallBackAdapter{
  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. Note that this method
  // displays the data encapsulated in the incoming 
  // object.
  public void recess(CallBackObjectClass obj){//announce
    System.out.println(name + obj.data);
  }//end overridden recess()
  //-----------------------------------------------------//

  //An object of the Teacher class can also invoke this 
  // method as a callback mechanism passing an object
  // as a parameter.
  public void lunch(CallBackObjectClass obj){//announce
    System.out.println(name + obj.data);
  }//end overridden lunch()
  //-----------------------------------------------------//
}//end class Student

//=======================================================//

//Another Class that implements the CallBack interface
// indirectly by extending the CallBackAdapter class.  
// See description above. This class ignores the recess()
// method as well as the dummy methods.

class Dog extends CallBackAdapter{
  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 a callback mechanism passing an object
  // as a parameter.
  public void lunch(CallBackObjectClass obj){//announce
    System.out.println(name + obj.data);
  }//end overridden lunch()
  //-----------------------------------------------------//
}//end class Dog
//=======================================================//

//A third Class that implements the CallBack interface
// indirectly by extending the CallBackAdapter class,
// similar to the other two classes.  This class ignores
// the lunch() method as well as the dummy methods.

class Cat extends CallBackAdapter{
  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, passing an object as a
  // parameter.
  public void recess(CallBackObjectClass obj){//announce
    System.out.println(name + obj.data);
  }//end overridden recess()
  //-----------------------------------------------------//
}//end class Cat
//=======================================================//

//Controlling class that ties all the pieces together and
// exercises them.
class Callback04{
  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 Callback04
//=======================================================//
-end-