The example below is a parallel to the way ActionListeners and WindowListeners work in the Java Swing/AWT libraries. Understanding this example will help you understand them better.
Let's say that we want to simulate a Classroom with a Teacher who has 20 Students. The basic fundamentals of our simulation is that Students will have the ability to raise and lower their hands. Additionally, the Students can be acknowledged (for raising their hands), and will then put their hand down.
The basic code for this simulation might look something like:
public class Classroom{
public static void main(String[] args){
Teacher t = new Teacher();
t.simulate();
}
}
import java.util.*;
public class Teacher{
private ArrayList<Student> students;
public Teacher(){
students = new ArrayList<Student>();
for (int i = 0; i < 20; i++)
students.add(new Student());
}
public void simulate(){
for(Student s : students)
s.simulate();
}
}
import java.util.*;
public class Student{
private static int nextID = 0;
private int id;
private boolean handRaised;
public Student(){
id = nextID++;
handRaised = false;
}
private void raiseHand(){
System.out.println(this + ": I'm raising my hand.");
handRaised = true;
}
private void lowerHand(){
System.out.println(this + ": I'm lowering my hand.");
handRaised = false;
}
public void acknowledged(){
if (handRaised){
System.out.println(this + ": I have been acknowledged, "
+ "and will I put my hand down.");
lowerHand();
}
}
public String toString(){
return "Student " + id;
}
public void simulate(){
//1 in 3 chance of raising hand
if (new Random().nextInt(3) == 0)
raiseHand();
}
}
So the fundamental problem that we have to address is:
So if the Students are going to tell the Teacher when they raise their hands, then the Teacher has to listen for the Students to do so.
StudentListener
And we end up with an interface that looks something like:
public interface StudentListener{
//the methods that go here are the things that the
//Student wants to communcate to anyone listening to it.
public void raisedHand(Student s);
public void loweredHand(Student s);
}
Note that the prototype of the methods take a Student
object as a parameter. This is important because it will allow a Student object to call these methods and pass itself as the parameter using this
.
This step is fairly straight forward, and we have done it several times already in the course. Simply put implements NameofListener
in the class definition, and then define the methods of the interface. You have do decide what the object that is listening to do in those methods when they are called.
Specific to this example:
public class Teacher implements StudentListener{
raisedHand(Student s)
method: public void raisedHand(Student s){
//put what you want the Teacher to do
//when a Student raises his/her hand
//The Student s in the parameter is a reference
//to the Student object that raised its hand.
}
loweredHand(Student s)
method: public void loweredHand(Student s){
//put what you want the Teacher to do
//when a Student lowers his/her hand
//The Student s in the parameter is a reference
//to the Student object that raised its hand.
}
And you might end up with something that looks like this when complete:
import java.util.*;
public class Teacher implements StudentListener{
private ArrayList<Student> students;
public Teacher(){
students = new ArrayList<Student>();
for (int i = 0; i <20; i++)
students.add(new Student());
//have this Teacher listen to each Student
for (Student s : students){
s.addStudentListener(this);
}
}
public void simulate(){
for(Student s : students)
s.simulate();
}
//what this Teacher does when it sees a
//Student raise his/her hand
public void raisedHand(Student s){
System.out.println(
"Teacher: I saw " + s +
" raise his/her hand up, " +
"and I acknowedged."
);
s.acknowledged();
}
//what this Teacher does when it see a
//Student lower his/her hand
public void loweredHand(Student s){
System.out.println(
"Teacher: I saw " + s +
" lower his/her hand down."
);
}
}
Now we go back to the class that is being listened to (aka the Student in this example), and we have to do a couple of things:
Specific to this example:
//a field to know who's listening to this student
private StudentListener sl;
StudenListener sl
For example://a method to add the StudentListerner for this
public void addStudentListener(StudentListener sl){
this.sl = sl;
}
sl.raisedHand(this)
. For example:private void raiseHand(){
System.out.println(
this + ": I'm raising my hand."
);
handRaised = true;
//tell whomever is watching/listening that
//this Student raised his/her hand
sl.raisedHand(this);
}
sl.loweredHand(this)
. For example:private void lowerHand(){
System.out.println(
this + ": I'm lowering my hand."
);
handRaised = false;
//tell whomever is watching/listening that
//this Student lowered his/her hand
sl.loweredHand(this);
}
And you might end up with something that looks like this when complete:
import java.util.*;
public class Student{
//nextID enables easy assignment of IDs to students
private static int nextID = 0;
private int id;
private boolean handRaised;
//a field to know who's listening to this student
private StudentListener sl;
public Student(){
id = nextID++;
handRaised = false;
sl = null;
}
//a method to add the StudentListerner for this
public void addStudentListener(StudentListener sl){
this.sl = sl;
}
private void raiseHand(){
System.out.println(
this + ": I'm raising my hand."
);
handRaised = true;
//tell whomever is watching/listening that
//this Student raised his/her hand
sl.raisedHand(this);
}
private void lowerHand(){
System.out.println(
this + ": I'm lowering my hand."
);
handRaised = false;
//tell whomever is watching/listening that
//this Student lowered his/her hand
sl.loweredHand(this);
}
public void acknowledged(){
if (handRaised){
System.out.println(this + ": I have been acknowledged, "
+ "and will I put my hand down.");
lowerHand();
}
}
public String toString(){
return "Student " + id;
}
public void simulate(){
//1 in 3 chance of raising hand
if (new Random().nextInt(3) == 0)
raiseHand();
}
}
Now the thing that remains is to go back to your implementing class and make one final change:
It needs to tell the subject objects that it's listening to them! In other words, it (the implementing class) needs to call the method to set itself (this
) as the Listener for that object.
So in our example, you might go back into Teacher.java and modify the constructor in the following manner:
Teacher(){
students = new ArrayList<Student>();
for (int i = 0; i <20; i++)
students.add(new Student());
//have this Teacher listen to each Student
for (Student s : students){
s.addStudentListener(this);
}
}
At this point you will have achieved success in getting your custom Listener to work. However, you are currently limited to allowing only ONE other object to listen to your object at any given time.
Remember this change:
//a field to know who's listening to this student
private StudentListener sl;
When in reality, there could be an untold number of things (of the same or class or different classes) that might want to listen to the same object.
In our example here, what if there were two Teachers watching the same group of Students; or what if the Teacher had an assistant?
This is no different than having multiple other GUI objects that are all listening to the same JButton for a click or to the same JTextField for new text to be entered.
So, what you need to do now, is go back into your "talking" class and modify the code to accept a limitless number of Listeners in the following manner:
ArrayList
makes this easy.)Specific to this example:
Change these:
//a field to know who's listening to this student
private StudentListener sl;
public Student(){
id = nextID++;
handRaised = false;
sl = null;
}
//a method to add the StudentListerner for this
public void addStudentListener(StudentListener sl){
this.sl = sl;
}
raiseHand()
method:private void raiseHand(){
System.out.println(
this + ": I'm raising my hand."
);
handRaised = true;
//tell whomever is watching/listening that
//this Student raised his/her hand
sl.raisedHand(this);
}
lowerHand()
method:private void lowerHand(){
System.out.println(
this + ": I'm lowering my hand."
);
handRaised = false;
//tell whomever is watching/listening that
//this Student lowered his/her hand
sl.loweredHand(this);
}
To these:
//a field to know who's listening to this student
private ArrayList<StudentListener> sls; //changed to ArrayList
public Student(){
id = nextID++;
handRaised = false;
sls = new ArrayList<StudentListener>(); //created the ArrayList
}
//a method to add the StudentListerner for this
public void addStudentListener(StudentListener sl){
sls.add(sl); //adding to the ArrayList
}
raiseHand()
method:private void raiseHand(){
System.out.println(
this + ": I'm raising my hand."
);
handRaised = true;
//tell whomever is watching/listening that
//this Student raised his/her hand
for (StudentListener sl : sls) //loop through the ArrayList
sl.raisedHand(this); //tell each Listener
}
lowerHand()
method:private void lowerHand(){
System.out.println(
this + ": I'm lowering my hand."
);
handRaised = false;
//tell whomever is watching/listening that
//this Student lowered his/her hand
for (StudentListener sl : sls) //loop through the ArrayList
sl.loweredHand(this); //tell each Listener
}
When it's all said and done, you'll have created and implemented your own fully funtional custom Listener. You can even add additional Listeners to your Students. For example, the final version below has added a TeachersAssistant class that is reponsible for counting the total number of students that raised their hand.
public class Classroom{
public static void main(String[] args){
Teacher t = new Teacher();
t.simulate();
}
}
public interface StudentListener{
//the methods that go here are the things that the
//Student wants to communcate to anyone listening to it.
public void raisedHand(Student s);
public void loweredHand(Student s);
}
import java.util.*;
public class Teacher implements StudentListener{
private ArrayList<Student> students;
private TeachersAssistant ta;
public Teacher(){
students = new ArrayList<Student>();
ta = new TeachersAssistant();
for (int i = 0; i <20; i++)
students.add(new Student());
//have this Teacher and his/her TeachersAssistant
//listen to the Students
for (Student s : students){
s.addStudentListener(this);
s.addStudentListener(ta);
}
}
public void simulate(){
for(Student s : students)
s.simulate();
System.out.println(ta);
}
//what this Teacher does when it sees a
//Student raise his/her hand
public void raisedHand(Student s){
System.out.println(
"Teacher: I saw " + s +
" raise his/her hand, " +
"and I acknowedged."
);
s.acknowledged();
}
//what this Teacher does when it see a
//Student lower his/her hand
public void loweredHand(Student s){
System.out.println(
"Teacher: I saw " + s +
" lower his/her hand."
);
}
}
public class TeachersAssistant implements StudentListener{
private int handCount = 0;
//what this TeachersAssistant does when it
//sees a Student raise his/her hand
public void raisedHand(Student s){
System.out.println(
"TA: I saw " + s +
" raise his/her hand up, " +
"and I incremented the count to " +
++handCount + "."
);
}
//what this TeachersAssistant does when it see a
//sees a Student lower his/her hand
public void loweredHand(Student s){
//nothing
}
public String toString(){
return "TA: I counted " +
handCount + " Students who raised their hand.";
}
}
import java.util.*;
public class Student{
//nextID enables easy assignment of IDs to students
private static int nextID = 0;
private int id;
private boolean handRaised;
//a field to know who's listening to this student
private ArrayList<StudentListener> sls; //changed to ArrayList
public Student(){
id = nextID++;
handRaised = false;
sls = new ArrayList<StudentListener>(); //created the ArrayList
}
//a method to add a StudentListerner for this
public void addStudentListener(StudentListener sl){
sls.add(sl); //adding to the ArrayList
}
private void raiseHand(){
System.out.println(
this + ": I'm raising my hand."
);
handRaised = true;
//tell whomever is watching/listening that
//this Student raised his/her hand
for (StudentListener sl : sls) //loop through the ArrayList
sl.raisedHand(this); //tell each Listener
}
private void lowerHand(){
System.out.println(
this + ": I'm lowering my hand."
);
handRaised = false;
//tell whomever is watching/listening that
//this Student lowered his/her hand
for (StudentListener sl : sls) //loop through the ArrayList
sl.loweredHand(this); //tell each Listener
}
public void acknowledged(){
if (handRaised){
System.out.println(this + ": I have been acknowledged, "
+ "and will I put my hand down.");
lowerHand();
}
}
public String toString(){
return "Student " + id;
}
public void simulate(){
//1 in 3 chance of raising hand
if (new Random().nextInt(3) == 0)
raiseHand();
}
}
You can run the above program with:
java ClassroomAnd you might see an output like:
bhawkins@faculty:~$ java Classroom Student 0: I'm raising my hand. Teacher: I saw Student 0 raise his/her hand, and I acknowedged. Student 0: I have been acknowledged, and will I put my hand down. Student 0: I'm lowering my hand. Teacher: I saw Student 0 lower his/her hand. TA: I saw Student 0 raise his/her hand up, and I incremented the count to 1. Student 10: I'm raising my hand. Teacher: I saw Student 10 raise his/her hand, and I acknowedged. Student 10: I have been acknowledged, and will I put my hand down. Student 10: I'm lowering my hand. Teacher: I saw Student 10 lower his/her hand. TA: I saw Student 10 raise his/her hand up, and I incremented the count to 2. Student 13: I'm raising my hand. Teacher: I saw Student 13 raise his/her hand, and I acknowedged. Student 13: I have been acknowledged, and will I put my hand down. Student 13: I'm lowering my hand. Teacher: I saw Student 13 lower his/her hand. TA: I saw Student 13 raise his/her hand up, and I incremented the count to 3. Student 16: I'm raising my hand. Teacher: I saw Student 16 raise his/her hand, and I acknowedged. Student 16: I have been acknowledged, and will I put my hand down. Student 16: I'm lowering my hand. Teacher: I saw Student 16 lower his/her hand. TA: I saw Student 16 raise his/her hand up, and I incremented the count to 4. TA: I counted 4 Students who raised their hand.