Un oggetto funtore è un oggetto il cui scopo principale è contenere un metodo.
Esistono diversi casi in cui occorre dire a un metodo di eseguire certe istruzioni. Un esempio sono le interfacce grafiche, in cui va specificato quale codice eseguire quando per esempio si preme un pulsante.
Alcuni linguaggi di programmazione consentono di passare direttamente delle sequenze di istruzioni a una subroutine. Altri come il C permettono di passare una funzione a un'altra funzione. Nel caso delle interfacce grafiche, si crea una funzione che contiene le istruzioni da eseguire in caso di pressione di un pulsante, e questa funzione viene passata alla funzione che specifica cosa fare in caso di pressione.
void mia_funzione() {
// istruzioni da eseguire alla pressione del pulsante
println("pulsante premuto!\n");
}
…
// creazione del pulsante b
b = crea_pulsante();
// eseguire mia_funzione quando si preme b
assegna_operazione(pulsnte, mia_funzione);
…
In Java, non si può passare un metodo a un altro metodo. Si può però passare un oggetto a un metodo. La soluzione è inserire il metodo da passare in un oggetto. Dal momento che questi oggetti hanno come scopo principale quello di contenere una funzione (un metodo), vengono detti oggetti funtore.
class Funtore implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
// istruzioni da eseguire alla pressione del pulsante
System.out.println("pulsante premuto!");
}
}
…
// creazione del pulsante b
b = new JButton("ok");
// eseguire f.actionPerformed(…) quando si preme b
Funtore f = new Funtore();
b.addActionListener(f);
…
Un altro esempio sono i thread. Un thread Posix in C lancia creando una funzione che contiene le istruzioni da eseguire e passandola alla funzione pthread_create().
void mia_funzione(void *arg) {
// istruzioni da eseguire nel thread
}
…
// lancia il thread
pthread_create(…, mia_funzione, …);
…
In Java, le istruzioni da eseguire vanno in un metodo run() che va in una classe. Un oggetto di questa classe si passa a new Thread().
class Funtore implements Runnable {
@Override
public void run() {
// istruzioni da eseguire nel thread
}
}
…
// lancia il thread
Funtore f = new Funtore();
Thread t = new Thread(f);
t.start();
…
La forma generale delle classi funtore è:
Un esempio: un thread the moltiplica due numeri.
class Moltiplica implements Runnable {
int a, b;
int ris;
@Override
public void run() {
this.ris = this.a * this.b;
}
}
L'istruzione di moltiplicazione viene eseguita in un thread separato. Prima si crea l'oggetto, si inseriscono i valori da moltiplicare nelle sue componenti a e b, si lancia il thread e si trova il risultato nella componente ris dell'oggetto.
Moltiplica m = new Moltiplica();
m.a = 2;
m.b = 3;
Thread t = new Thread(m);
t.start();
…
System.out.println("risultato: " + m.ris);
La versione incapsulata del funtore:
class Moltiplica implements Runnable {
private int a, b;
private int ris;
public Moltiplica(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
this.ris = this.a * this.b;
}
public getRis() {
return this.ris;
}
}
…
Moltiplica m = new Moltiplica(2, 3);
Thread t = new Thread(m);
t.start();
…
System.out.println("risultato: " + m.getRis());
…
La versione incapsulata permette di controllare che run() sia stato eseguito quando viene richiesto il risultato con getRis().
class Moltiplica implements Runnable {
private int a, b;
private int ris;
private boolean eseguita;
public Moltiplica(int a, int b) {
this.a = a;
this.b = b;
this.eseguita = false;
}
@Override
public synchronized void run() {
if (this.eseguita)
return;
this.eseguita = true;
this.ris = this.a * this.b;
}
public synchronized getRis() {
if (! this.eseguita)
throw new RuntimeException("moltiplicazione ancora non eseguita");
return this.ris;
}
}
I metodi run() e getRis() devono essere sincronizzati dato che accedono alla stessa variabile this.ris ma in thread diversi: il primo viene eseguito nel thread separato, il secondo nel thread principale.
Al contrario dei metodi semplici, un funtore può avere più di un valore di ritorno. Un esempio è il calcolo di somma e moltiplicazione nello stesso metodo.
class Moltiplica implements Runnable {
private int a, b;
private int s, m;
private boolean eseguita;
public Moltiplica(int a, int b) {
this.a = a;
this.b = b;
this.eseguita = false;
}
@Override
public synchronized void run() {
if (this.eseguita)
return;
this.eseguita = true;
this.s = this.a + this.b;
this.m = this.a * this.b;
}
public synchronized getSomma() {
if (! this.eseguita)
throw new RuntimeException("moltiplicazione ancora non eseguita");
return this.s;
}
public synchronized getProdotto() {
if (! this.eseguita)
throw new RuntimeException("moltiplicazione ancora non eseguita");
return this.m;
}
}