Per seguire il funzionamento degli eventi in java bisogna definire un evento personalizzato, estendendo la classe EventObject. Esempio:
Codice PHP:
class MyEvent extends EventObject{
public MyEvent(Object source){
super(source);
}
//piu eventuali altri metodi pubblici...
}
Quindi si definisce un'interfaccia che estende EventListener e che definisce quali metodi devono essere implementati da un ascoltatore di tale evento:
Codice PHP:
interface MyEventListener extends EventListener{
//Metodi da implementare per essere un ascoltatore di MyStatusEvent
public void myStatusChangedEvent(MyEvent e);
//eventuali altri metodi
}
Per mettersi in ascolto di un evento bisogna registrarsi come un ascoltatore sull'oggetto che può sollevare l'evento: la classe che solleva gli eventi deve quindi offrire dei metodi per registrare e deregistrare un listener:
Codice PHP:
public void addMyEventListener(MyEventListener l);
e
Codice PHP:
public void removeMyEventListener(MyEventListener l);
I listener possono essere raccogliere in una EventListenerList; ogni volta che bisogna generare l'evento, si crea un MyEvent evt, passandogli ad esempio come argomento il riferimento this (cioè l'oggetto che sta sollevando l'oggetto), e per ogni listener registrato, si invoca il metodo myStatusChanchedEvent(evt).
In particolare, visto che possono essere sollevati eventi in piu punti del codice è utile implementare un metodo
private fireMyStatusChangedEvent(....)
che si occupa di invocare myStatusChangedEvent(evt) per ogni listener presente nella lista dei listeners.
Ovviamente nella classe MyEvent possono essere definiti altri metodi pubblici per ricavare informazioni sull'evento, come ad esempio metodi che forniscono il valore del nuovo stato dell'oggetto. Dato che MyEvent estende EventObject, su un MyEvent evt può essere invocato il metodo
Object getSource();
per avere l'oggetto che ha lanciato l'evento, e che è stato passato al costruttore di MyEvent al momento della costruzione dell'evento.
Esempio
Data una classe che rappresenta il modello di una persona con nome e anno di nascita e vogliamo implementare il modello di eventi ed ascoltatori per ricevere le notifiche delle modifiche del nome e dell'anno.
Codice PHP:
import java.util.EventListener;
import java.util.EventObject;
import javax.swing.event.EventListenerList;
class PersonEvent extends EventObject{
Person person;
Object oldValue;
Object newValue;
public PersonEvent(Person source, Object oldValue, Object newValue){
super(source);
this.person = source;
this.oldValue = oldValue;
this.newValue = newValue;
}
public Person getPerson(){
return person;
}
public Object getNewValue(){
return newValue;
}
public Object getOldValue(){
return oldValue;
}
}
interface PersonListener extends EventListener{
public void nameChanged(PersonEvent event);
public void yearChanged(PersonEvent event);
}
public class Person{
private String name;
private int yearOfBirth;
private EventListenerList listeners;
public Person(String name, int yearOfBirth){
this.name = name;
this.yearOfBirth = yearOfBirth;
listeners = new EventListenerList();
}
public String getName(){
return name;
}
public void setName(String newName){
if(name.equals(newName))
return;
String oldName = this.name;
name = newName;
fireNameChangedEvent(oldName, newName);
}
private void fireNameChangedEvent(String oldName, String newName){
PersonEvent event = new PersonEvent(this, oldName, newName);
Object[] listenersArray = listeners.getListenerList();
for(int i = listenersArray.length - 2; i >= 0; i -= 2){
if(listenersArray[i] == PersonListener.class){
((PersonListener)listenersArray[i+1]).nameChanged(event);
}
}
}
public int getYearOfBirth(){
return yearOfBirth;
}
public void setYearOfBirth(int newYearOfBirth){
if(newYearOfBirth == yearOfBirth)
return;
int oldYearOfBirth = yearOfBirth;
yearOfBirth = newYearOfBirth;
fireYearChangedEvent(oldYearOfBirth, newYearOfBirth);
}
private void fireYearChangedEvent(int oldYear, int newYear){
PersonEvent event = new PersonEvent(this,
new Integer(oldYear), new Integer(newYear));
Object[] listenersArray = listeners.getListenerList();
for(int i = listenersArray.length - 2; i >= 0; i -= 2){
if(listenersArray[i] == PersonListener.class){
((PersonListener)listenersArray[i+1]).yearChanged(event);
}
}
}
public void addPersonListener(PersonListener listener){
listeners.add(PersonListener.class, listener);
}
public void removePersonListener(PersonListener listener){
listeners.remove(PersonListener.class, listener);
}
}
class SimplePersonListener implements PersonListener{
public void nameChanged(PersonEvent event){
System.out.println("Ricevuto evento di modifica del nome:");
System.out.println("\tvecchio valore: " + event.getOldValue());
System.out.println("\tnuovo valore: " + event.getNewValue());
System.out.println();
}
public void yearChanged(PersonEvent event){
System.out.println("Ricevuto evento di modifica del'annod i nascita:");
System.out.println("\tvecchio valore: " + event.getOldValue());
System.out.println("\tnuovo valore: " + event.getNewValue());
System.out.println();
}
}
class ListenersMain{
public static void main(String[] args){
Person p = new Person("Marco", 1980);
PersonListener listener = new SimplePersonListener();
p.addPersonListener(listener);
p.setName("Adrea");
p.setYearOfBirth(1985);
//non produce evento
p.setYearOfBirth(1985);
}
}
Output stampato dall'esempio:
Codice PHP:
Ricevuto evento di modifica del nome:
vecchio valore: Marco
nuovo valore: Adrea
Ricevuto evento di modifica del'annod i nascita:
vecchio valore: 1980
nuovo valore: 1985
Notiamo che la classe Person non ha alcun riferimento diretto ai suoi listener, e ignora quali siano le classi a cui essi appartengono. Ciò permette anche di collegare lo stesso oggetto Person a più listeners contemporaneamente in maniera del tutto trasparente all'oggetto Person, ovvero lo stesso oggetto può fare da modello contemporaneamente per più componenti grafici che restano cosi tutti sincronizzati. Ad esempio, in una interfaccia grafica potremmo registrare come listener di un oggetto Person un campo di testo e una JSlider che permettono entrambe di editare l'anno di nascita: le modifiche sull'uno si ripercuoteranno automaticamente sull'altra e viceversa.