Eventi Personalizzati

Concetti base

 

Oggetto: Come creare propri tipi di eventi da lanciare a proprio piacimento

 

Premesso che gli attori di questo meccanismo sono:

  • L'evento - che verrà lanciato dal sorgente
  • L'interfaccia - che definisce quali metodi devono essere implementati da un ascoltatore di tale evento
  • Il sorgente - la classe che solleva gli eventi e che deve offrire dei metodi per registrare e deregistrare un listener:
    public void addMyEventListener(MyEventListener l);
    public void removeMyEventListener(MyEventListener l);
  • L'osservatore - (Listener) che riceverà l'evento e che eseguirà le azioni di reazione nei metodi che l'interfaccia gli ha chiesto di implementare

Per permettere il colloquio tra due classi (oggetti) bisogna che queste implementino ciascuna i seguenti elementi:

 

Il file EsecutorN2.java penserà ad eseguire il tutto.

 

Il file Persona.java è il sorgente che implementerà:

  • La lista Ascoltatori
  • il metodo fire
  • il metodo add
  • il metodo remove

Il file PersonaEvent.java conterra:

  • l'Evento
  • l'Interfaccia
  • l'Ascoltatore

In realta ci sono altre combinazioni ma noi prenderemo noi considerazione questa.

Ad es. mentre il sorgente il file Persona.java deve come minimo implementare le 4 parti di cui sopra, le altre 3 parti possono anche essere implementate in file diversi ad esempio:

  • PersonaEvento.java
  • personaInterfaccia.java
  • Ascoltatore.java

Ma in questo esempio adotteremo il primo caso.

In questo progetto avviene che quando si cambia la proprietà nome del sorgente viene lanciato un evento che trasporta con se tre informazioni (i suoi tre argomenti), il primo è il sorgente che lo ha lanciato, il secondo è il vecchio nome ed il terzo è il nupvo nome. Qui abbiamo due ascoltatori che vengono associati al sorgente e che rimangono in attesa che questo gli invii l'oggetto evento. Quando ciò accade l'ascoltatore eseguirà un azione, scriverà sul terminale le tre informazioni ricevute ossia il nome del sorgente, il vecchio nome ed il nuovo nome.

L'esecutore, come il suo nome suggerisce, istanzia l'oggetto Persona (il sorgente) e nel farlo gli assegna il primo nome "Silvano" e l'età 70, istanzia l'ascoltatore, lo associa al sorgente (Persona) e va a modificare il nome con il metodo setName di Persona cambiandolo in "Andrea". Questo scatena il lancio dell'evento che raggiunge l'Ascoltatore il quale esegue l'azione: scrivere i dati sul terminale. Se si imposta di nuovo un nome l'evento verrà lanciato solo se il nome è diverso. Nel codice sotto questo non accadrà perchè abbiamo scritto lo stesso nome e quindi non è avvenuta nessuna modifica e nessun evento verràlanciato.

 

Vediamo cosa fa l'esecutore:

  • Istanzia il sorgente p
  • Istanzia i due ascoltatori uno asolterà per il cambio del nome e l'altro per il cambio dell'età
  • Associa i listener appena creati al source p
  • Passa a modificare le due proprietà e questo come vedremo provocherà il lancio degli eventi

Classe: EsecPersonEvent

 1 package esecpersonevent;
 2 
 3 class EsecPersonEvent{
 4     public static void main(String[] args){
 5         Person p = new Person("Silvano", 70);
 6         nameListener nmlistener = new nameListener();
 7         yearListener yelistener = new yearListener();
 8         p.addNamePersonListener(nmlistener);
 9         p.addYearPersonListener(yelistener);
10         p.setName("Adrea"); 
11         p.setYear(35);
12         
13         System.out.println(p.getOldName() + " ha un'età di " + p.getOldYear() + " anni");
14         System.out.println(p.getName() + " ha un'età di " + p.getYear() + " anni");
15     }
16 }

 

Cosa fa il Source:

 

Dichiara un oggetto di tipo EventListenerList di nome listeners. che in realtà non è altro che un array.
e lo istanzia:

private final EventListenerList listeners;
listeners = new EventListenerList();

 

Il costruttore prepara i due argommenti da ricevere nome ed età e li assegna alle variabili private

Seguono tutti i metodi per scrivere e leggere le proprirtà. e quindi avremo:

getName, setName, getYear, setYear,getOldName, getOldYear, getObjName.

Ma quelle più interessanti sono setName e setYear.

Commenterò solo setName l'altra è simile.

Deve impostare un nuovo nome che riceve come argomento, prima controlla se il nome inserito newName è uguale al nome attuale se così è non farà nulla (uscirà con return) se invece è diverso metterà da parte il nome attuale in oldName e modificherà quello atuale con il nuovo.

lancia il metodo fire, ma quello giustoperò, ossia fireNameChangedEvent.

 

27     public void setName(String newName){
28         if(name.equals(newName))
29             return;
30         
31         oldName = name;
32         name = newName;
33         fireNameChangedEvent(oldName, newName);
34     }  

 

Prima di addentrarci nella spiegazione del metodo fire direi due parole sui metodi add:

 

75     public void addNamePersonaListener(nameListener listener){
76         listeners.add(namePersonaListener.class, listener);
77     }

 

Un'EventListenerList è un particolare tipo di arrayList che hanno un metodo add, diciamo potenziato, cioè in grado di aggiungere due elementi alla volta. Ricordo che un ArrayList normale può aggiungere con il suo metodo add solo un elemento alla volta. Il metodo AddNamePersonaListener() ha un solo argomento, può ricevere un listener ma attenzione di tipo nameListener (quello che ha un solo metodo nameChanged) se proviamo a passargli listener di altro tipo il copilatore ci da errore. per cui siamo sicuri al 100% che il listener passato sia di quel tipo. Il compito di questo metodo è di aggiungere due elementi al listeners, il primo che viene accodato è namePersonaListener.class, questo è un oggetto che identifica il tipo di listener che abbiamo ricevuto, poi accoda nel secondo elemento l'istanza del listener passato come argomento, insomma l'oggettto nmlistener.

Quando l'esecutore usa l'altro add e cioè: addYearPersonaListener allora, poichè questo può ricevere come argomento solo un listener di tipo yearListener (quello che ha un solo metodo yearChanged) allora addYearPersonaListener accodera altri due elementi al listeners, ma questa volta l'identificatore di tipo sarà: yearPersonaListener.class e l'oggetto listener sarà: yeListener.

A questo punto il listeners è caricato e ha 4 elementi i primi due si riferiscono al listener nmListener e gli ultimii due al listener yeListener. In pratica [tipo][listener][tipo][listener].

 

Bene ora possiamo passare al metodo fire che sono anchessi due:

 

private void fireNameChangedEvent(String oldName, String newName)

private void fireYearChangedEvent(int oldYear, int newYear)

 

Commenterò solo il primo l'altro è simile.

 

57     private void fireNameChangedEvent(String oldName, String newName){
58         PersonaEvent event = new PersonaEvent(this, oldName, newName);
59         Object[] listenersArray = listeners.getListenerList();
60         for(int i = listenersArray.length - 2; i >= 0; i -= 2){
61             if(listenersArray[i] == namePersonaListener.class){
62             ((namePersonaListener)listenersArray[i+1]).nameChanged(event);
63             }
64         }
65     }

 

come si può vedere questo metodo nel nostro caso ha due argomenti due stringhe oldName e newName, sono le informazione che gli serviranno per creare un oggetto evento. Ed infatti la prim cosa che fa è proprio istanziare un oggetto evento riga 58. IL this sta ad indicare la classe di apparteneza cioè Persona.

alla riga59 si fa una bella copia dell'EventArrayListener non si sa mai dovesse rovinarsi.

Con il ciclo for andrà alla ricerca iniziando dall'ultima coppia di elementi. Ragioniamo un attimo sul ciclo for. Prima imposta la i alla lunghezza dell'array -2 cosa significa: nel nostro caso la lunghezza è pari a 4, 4-2 fa 2. L'indice dell'elemento sara 0|1|2|3. se la lunghzza fosse stata 10 allora 10 - 2 = 8 percui. 0|1|2|3|4|5|6|7|8|9. Come vedete la i è inizialmente puntata sul primo elemento dell'ultima coppia. Per i>=0 cioè finché il valore di i si mantiene superiore od uguale a zero il ciclo for può iterare. i-=2 questo equivale a dire i=i-2 cioè prima si sottrae a i 2 e viene nella prima iterazione i diventa 0 (2-2=0) l'indice 0 sarà 0|1|2|3 quindi la seconda volta l'indice si sposterà a 0 e questa è la prma posizione della prima coppia di dati. Quindi riassumendo la prima volta del ciclo for si andrà a leggere il tipo del secondo listener quello introdotto per ultimo, la seconda volta andtrà a leggere il tipo della prima coppia, quella introdotta per per prima. Il terzo ciclo il ciclo for termina poiché la condizione non è più rispettata infatti i che è 0-2=-2 r -2 non è più <=0.

ciclo 1 -- i=2 -- i>x=0 SI esegui il blocco {} -- poi decrementa i-=2 -> ì=i-2 -> 2-2=0

ciclo 2 -- i=0 -- i>x=0 SI esegui il blocco {} -- poi decrementa i-=0 -> ì=i-2 -> 0-2=-2

ciclo 3 -- i=-2 -- i>x=0 NO esci dal ciclo for

Allora al ciclo 1 estrae il tipo dell'utima coppia che è yearPersonaListener.class e lo confronta con il tipo che sta cercando ossia: namePersonaListener.class e si chiede sono uguali se SI esegue il blocco se NO passa al ciclo 2. ne nostro caso è NO si va al prossimo giro. Si va a leggere ora il tipo della prima coppia inserita che è

namePersonaListener.class e poichè questa volta corrisponde a quello che cercava esegue il blocco.

Coasa si fa nel blocco? si sposta l'indice di un posto che da 0 diventa 1 siamo a:0|1|2|3 cosa c'è in questo elemento? c'è nmListener proprio in listener che volevamo ed allora lo triamo fuori ma dobbiamo necessariamente fare un casting del tipo perchè il listenerArray è un array di Object quindi generico lo dobbiamo forzare a namePersonaListener come con le parentesi tonde. Bene ora che lo abbiamo tirato fuori gli facciamo eseguire il metodo nameChanged passandogli l'evento riga 62 e Boom il listener compie la sua azione.

Fantastico non trovate?

 

Seguono le due classi:

Classe:Person

 1 package esecpersonevent;
 2 
 3 import javax.swing.event.EventListenerList;
 4 
 5 public class Person{
 6     private String name, oldName;
 7     private int year,oldYear;
 8     private final String obNam="Persona";
 9     private final EventListenerList listeners;
10     
11     //Costruttore
12     public Person(String name,  int year){
13         this.name = name; 
14         this.year = year;
15         listeners = new EventListenerList();
16     }
17     
18     //Le proprietà--------------------
19     public String getObjName(){   
20         return obNam;
21     }
22     
23     public String getName(){
24         return name;
25     }
26 
27     public void setName(String newName){
28         if(name.equals(newName))
29             return;
30         
31         oldName = name;
32         name = newName;
33         fireNameChangedEvent(oldName, newName);
34     }  
35     
36     public int getYear(){
37         return year;
38     }
39     
40     public void setYear(int newYear){
41         if(year == newYear)
42             return;
43         
44         oldYear = year;
45         year = newYear;
46         fireYearChangedEvent(oldYear, newYear);
47     }  
48     public String getOldName(){
49         return oldName;
50     }
51     public int getOldYear(){
52         return oldYear;
53     }   
54     //End Proprietà ----------------------
55     
56     // I Metodi --------------------------
57     private void fireNameChangedEvent(String oldName, String newName){
58         PersonEvent event = new PersonEvent(this, oldName, newName);
59         Object[] listenersArray = listeners.getListenerList();
60         for(int i = listenersArray.length - 2; i >= 0; i -= 2){
61             if(listenersArray[i] == namePersonListener.class){
62             ((namePersonListener)listenersArray[i+1]).nameChanged(event);
63             }
64         }
65     }
66     private void fireYearChangedEvent(int oldYear, int newYear){
67         PersonEvent event = new PersonEvent(this, oldYear, newYear);
68         Object[] listenersArray = listeners.getListenerList();
69         for(int i = listenersArray.length - 2; i >= 0; i -= 2){
70             if(listenersArray[i] == yearPersonListener.class){
71             ((yearPersonListener)listenersArray[i+1]).yearChanged(event);
72             }
73         }
74     }   
75     public void addNamePersonListener(nameListener listener){
76         listeners.add(namePersonListener.class, listener);
77     }
78     public void addYearPersonListener(yearListener listener){
79         listeners.add(yearPersonListener.class, listener);
80     }    
81      public void removeNamePersonListener(nameListener listener){
82         listeners.remove(namePersonListener.class, listener);
83     }
84      public void removeYearPersonListener(yearListener listener){
85         listeners.remove(yearPersonListener.class, listener);
86     }
87     //End Metodi 
88 }

 

 

Classe:PersonEvent

 1 package esecpersonevent;
 2 
 3 import java.util.EventListener;
 4 import java.util.EventObject;
 5 
 6 // l'Evento
 7 class PersonEvent extends EventObject{
 8     Person person;
 9     Object oldValue;
10     Object newValue;
11     
12     //Costruttore
13     public PersonEvent(Person source, Object oldValue, Object newValue){
14         super(source);
15         person = source;
16         this.oldValue = oldValue;
17         this.newValue = newValue;
18         }
19     
20     //Proprietà -------------------
21     public Person getPerson(){
22         return person;
23     }
24     public Object getNewValue(){
25         return newValue;
26     }
27     public Object getOldValue(){
28         return oldValue;
29     }
30     //End Proprietà -----------------
31 }
32 
33 // Le Interfacce -------------------
34 interface namePersonListener extends EventListener{
35     public void nameChanged(PersonEvent event);
36 }
37 interface yearPersonListener extends EventListener{
38     public void yearChanged(PersonEvent event);
39 }
40 //End Interfacce -------------------
41 
42 // Gli Ascoltatori -----------------
43 class nameListener implements namePersonListener{    
44     @Override
45     public void nameChanged(PersonEvent event){       
46         Person person = event.getPerson();
47         String objStr = person.getObjName();
48         System.out.println("Ricevuto evento di modifica del nome da:" +objStr);
49         System.out.println("\tvecchio valore: " + event.getOldValue());
50         System.out.println("\tnuovo valore: " + event.getNewValue());
51         System.out.println();
52     }    
53 }
54 class yearListener implements yearPersonListener{    
55     @Override
56     public void yearChanged(PersonEvent event){       
57         Person person = event.getPerson();
58         String objStr = person.getObjName();
59         System.out.println("Ricevuto evento di modifica del nome da:" +objStr);
60         System.out.println("\tvecchio valore: " + event.getOldValue());
61         System.out.println("\tnuovo valore: " + event.getNewValue());
62         System.out.println();
63     }
64     //End Ascoltatori ---------------    
65 }

 

Questo l'output:

Ricevuto evento di modifica del nome da:Persona
vecchio valore: Silvano
nuovo valore: Adrea

 

Ricevuto evento di modifica del nome da:Persona
vecchio valore: 70
nuovo valore: 35

 

Silvano ha un'età di 70 anni
Adrea ha un'età di 35 anni