Espressioni Lambda come Argomenti in Java
- Le espressioni lambda in Java possono essere passate come argomenti a metodi, migliorando la flessibilità del codice.
- Per passare un'espressione lambda come argomento, il tipo del parametro del metodo deve essere compatibile con l'interfaccia funzionale della lambda.
- Le lambda possono essere utilizzate per operazioni su stringhe, numeri e altri tipi di dati, rendendo il codice più conciso e leggibile.
- È possibile utilizzare sia lambda a espressione che lambda a blocco come argomenti di metodi, a seconda della complessità dell'operazione da eseguire.
Passaggio di Espressioni Lambda come Argomenti
Come spiegato nelle lezioni precedenti, un'espressione lambda può essere utilizzata in qualsiasi contesto che fornisca un tipo target.
Uno di questi è quando un'espressione lambda viene passata come argomento di metodi. In effetti, passare un'espressione lambda come argomento di metodi è un uso comune delle lambda. Inoltre, è un uso molto potente perché fornisce un modo per passare codice eseguibile come argomento a un metodo. Questo migliora notevolmente il potere espressivo di Java.
Per passare un'espressione lambda come argomento, il tipo del parametro che riceve l'argomento dell'espressione lambda deve essere di un tipo di interfaccia funzionale compatibile con la lambda.
Chiariamo il tutto attraverso un esempio. Consideriamo il seguente programma che utilizza un'espressione lambda come argomento di un metodo:
// Utilizzare espressioni lambda come argomento a un metodo.
// Un'interfaccia funzionale che dichiara il metodo fuzione().
// Questa interfaccia funzionale accetta una stringa e restituisce una stringa.
interface FunzioneStringa {
String funzione(String n);
}
class DemoLambdaComeArgomenti {
// Questo metodo ha un'interfaccia funzionale come tipo del
// suo primo parametro.
// Pertanto, si può passareun riferimento a
// qualsiasi istanza di quell'interfaccia, inclusa l'istanza creata
// da un'espressione lambda.
// Il secondo parametro specifica la stringa su cui operare.
static String operazioneStringa(FunzioneStringa fs, String s) {
return fs.funzione(s);
}
// Il metodo main() dimostra l'uso di operazioneStringa().
public static void main(String[] args)
{
String stringaInput = "Le lambda aggiungono potenza a Java";
String stringaOutput;
System.out.println("Ecco la stringa di input: " + stringaInput);
// Qui, dichiariamo una semplice espressione lambda
// che converte una stringa in maiuscolo e
// viene passata a operazioneStringa().
stringaOutput = operazioneStringa(
(str) -> str.toUpperCase(),
stringaInput);
System.out.println("La stringa in maiuscolo: " + stringaOutput);
// Qui, dichiariamo una lambda a blocco che rimuove
// gli spazi da una stringa e viene passata a operazioneStringa().
// Poiché il corpo della lambda è un blocco, è necessario
// usare l'istruzione return per restituire il risultato.
stringaOutput = operazioneStringa(
(str) -> {
String risultato = "";
int i;
for(i = 0; i < str.length(); i++)
if(str.charAt(i) != ' ')
risultato += str.charAt(i);
return risultato;
}, stringaInput);
System.out.println("La stringa con gli spazi rimossi: " + stringaOutput);
// Naturalmente, è anche possibile passare un'istanza di FunzioneStringa
// creata da un'espressione lambda precedente.
// Ad esempio, dopo che questa dichiarazione viene eseguita,
// inverti si riferisce a un'istanza di FunzioneStringa.
FunzioneStringa inverti = (str) -> {
String risultato = "";
int i;
for(i = str.length()-1; i >= 0; i--)
risultato += str.charAt(i);
return risultato;
};
// Ora, inverti può essere passata come primo parametro
// a operazioneStringa() poiché si riferisce
// a un oggetto FunzioneStringa.
System.out.println("La stringa invertita: " +
operazioneStringa(inverti, stringaInput));
}
}
L'output è mostrato qui:
Ecco la stringa di input: Le lambda aggiungono potenza a Java
La stringa in maiuscolo: LE LAMBDA AGGIUNGONO POTENZA A JAVA
La stringa con gli spazi rimossi: LelambdaaggiungonopotenzaaJava
La stringa invertita: avaJ a aznetop onognuigga adbmal eL
Nel programma, notiamo prima il metodo operazioneStringa()
. Ha due parametri. Il primo è di tipo FunzioneStringa
, che è un'interfaccia funzionale. Quindi, questo parametro può ricevere un riferimento a qualsiasi istanza di FunzioneStringa
, inclusa una creata da un'espressione lambda. Il secondo argomento di operazioneStringa()
è di tipo String
, e questa è la stringa su cui si opera.
Successivamente, notiamo la prima chiamata a operazioneStringa()
, mostrata di nuovo qui:
stringaOutput = operazioneStringa(
(str) -> str.toUpperCase(),
stringaInput);
Qui, una semplice espressione lambda viene passata come argomento. Quando ciò accade, viene creata un'istanza dell'interfaccia funzionale FunzioneStringa
e un riferimento a quell'oggetto viene passato al primo parametro di operazioneStringa()
. Quindi, il codice lambda, incorporato in un'istanza di classe, viene passato al metodo. Il contesto del tipo target è determinato dal tipo del parametro. Poiché l'espressione lambda è compatibile con quel tipo, la chiamata è valida. Incorporare lambda semplici, come quella appena mostrata, all'interno di una chiamata di metodo è spesso una tecnica conveniente—specialmente quando l'espressione lambda è destinata a un uso singolo.
Successivamente, il programma passa una lambda a blocco a operazioneStringa()
. Questa lambda rimuove gli spazi da una stringa. È mostrata di nuovo qui:
stringaOutput = operazioneStringa(
(str) -> {
String risultato = "";
int i;
for(i = 0; i < str.length(); i++)
if(str.charAt(i) != ' ')
risultato += str.charAt(i);
return risultato;
}, stringaInput);
Sebbene questa espressione usi una lambda a blocco, il processo di passaggio dell'espressione lambda è lo stesso di quello appena descritto per la semplice espressione lambda. In questo caso, tuttavia, alcuni programmatori troveranno la sintassi un po' scomoda.
Quando una lambda a blocco sembra eccessivamente lunga da incorporare in una chiamata di metodo, è facile assegnare quella lambda a una variabile di interfaccia funzionale, come hanno fatto gli esempi precedenti. Quindi, si può semplicemente passare quel riferimento al metodo.
Questa tecnica è mostrata alla fine del programma. Lì, viene definita una lambda a blocco che inverte una stringa. Questa lambda è assegnata a inverti
, che è un riferimento a un'istanza di FunzioneStringa
. Quindi, inverti
può essere usato come argomento per il primo parametro di operazioneStringa()
. Il programma quindi chiama operazioneStringa()
, passando inverti
e la stringa su cui operare. Poiché l'istanza ottenuta dalla valutazione di ogni espressione lambda è un'implementazione di FunzioneStringa
, ognuna può essere usata come primo parametro per operazioneStringa()
.
Un ultimo punto: Oltre all'inizializzazione di variabili, all'assegnazione e al passaggio di argomenti, anche i seguenti costituiscono contesti di tipo target: cast, l'operatore ?
, inizializzatori di array, istruzioni return
e le espressioni lambda stesse.