Groovy - Chiusure

Una chiusura è un breve blocco anonimo di codice. Normalmente si estende su poche righe di codice. Un metodo può anche prendere il blocco di codice come parametro. Sono di natura anonima.

Di seguito è riportato un esempio di una semplice chiusura e come appare.

class Example {
   static void main(String[] args) {
      def clos = {println "Hello World"};
      clos.call();
   } 
}

Nell'esempio precedente, la riga di codice - {println "Hello World"} è nota come chiusura. Il blocco di codice a cui fa riferimento questo identificatore può essere eseguito con l'istruzione call.

Quando eseguiamo il programma sopra, otterremo il seguente risultato:

Hello World

Parametri formali nelle chiusure

Le chiusure possono anche contenere parametri formali per renderle più utili proprio come i metodi in Groovy.

class Example {
   static void main(String[] args) {
      def clos = {param->println "Hello ${param}"};
      clos.call("World");
   } 
}

Nell'esempio di codice precedente, notare l'uso di $ {param} che fa sì che la chiusura prenda un parametro. Quando si chiama la chiusura tramite l'istruzione clos.call, ora abbiamo la possibilità di passare un parametro alla chiusura.

Quando eseguiamo il programma sopra, otterremo il seguente risultato:

Hello World

L'illustrazione successiva ripete l'esempio precedente e produce lo stesso risultato, ma mostra che un singolo parametro implicito a cui si fa riferimento può essere utilizzato. Qui "it" è una parola chiave in Groovy.

class Example {
   static void main(String[] args) {
      def clos = {println "Hello ${it}"};
      clos.call("World");
   } 
}

Quando eseguiamo il programma sopra, otterremo il seguente risultato:

Hello World

Chiusure e variabili

Più formalmente, le chiusure possono riferirsi a variabili nel momento in cui viene definita la chiusura. Di seguito è riportato un esempio di come questo può essere ottenuto.

class Example {     
   static void main(String[] args) {
      def str1 = "Hello";
      def clos = {param -> println "${str1} ${param}"}
      clos.call("World");
		
      // We are now changing the value of the String str1 which is referenced in the closure
      str1 = "Welcome";
      clos.call("World");
   } 
}

Nell'esempio sopra, oltre a passare un parametro alla chiusura, stiamo anche definendo una variabile chiamata str1. La chiusura assume anche la variabile insieme al parametro.

Quando eseguiamo il programma sopra, otterremo il seguente risultato:

Hello World 
Welcome World

Utilizzo delle chiusure nei metodi

Le chiusure possono anche essere utilizzate come parametri per i metodi. In Groovy, molti metodi incorporati per i tipi di dati come elenchi e raccolte hanno chiusure come tipo di parametro.

L'esempio seguente mostra come inviare una chiusura a un metodo come parametro.

class Example { 
   def static Display(clo) {
      // This time the $param parameter gets replaced by the string "Inner"         
      clo.call("Inner");
   } 
	
   static void main(String[] args) {
      def str1 = "Hello";
      def clos = { param -> println "${str1} ${param}" }
      clos.call("World");
		
      // We are now changing the value of the String str1 which is referenced in the closure
      str1 = "Welcome";
      clos.call("World");
		
      // Passing our closure to a method
      Example.Display(clos);
   } 
}

Nell'esempio sopra,

  • Stiamo definendo un metodo statico chiamato Display che accetta una chiusura come argomento.

  • Stiamo quindi definendo una chiusura nel nostro metodo principale e passandola al nostro metodo Display come parametro.

Quando eseguiamo il programma sopra, otterremo il seguente risultato:

Hello World 
Welcome World 
Welcome Inner

Chiusure in collezioni e stringhe

Diversi metodi List, Map e String accettano una chiusura come argomento. Vediamo un esempio di come le chiusure possono essere utilizzate in questi tipi di dati.

Utilizzo di chiusure con elenchi

L'esempio seguente mostra come è possibile utilizzare le chiusure con gli elenchi. Nell'esempio seguente definiamo prima un semplice elenco di valori. Il tipo di raccolta elenco definisce quindi una funzione chiamata.each. Questa funzione assume come parametro una chiusura e applica la chiusura a ogni elemento della lista.

class Example {
   static void main(String[] args) {
      def lst = [11, 12, 13, 14];
      lst.each {println it}
   } 
}

Quando eseguiamo il programma sopra, otterremo il seguente risultato:

11 
12 
13 
14

Utilizzo di chiusure con mappe

L'esempio seguente mostra come è possibile utilizzare le chiusure con Maps. Nell'esempio seguente definiamo prima una semplice mappa di elementi di valore chiave. Il tipo di raccolta di mappe definisce quindi una funzione chiamata .each. Questa funzione assume una chiusura come parametro e applica la chiusura a ciascuna coppia chiave-valore della mappa.

class Example {
   static void main(String[] args) {
      def mp = ["TopicName" : "Maps", "TopicDescription" : "Methods in Maps"]             
      mp.each {println it}
      mp.each {println "${it.key} maps to: ${it.value}"}
   } 
}

Quando eseguiamo il programma sopra, otterremo il seguente risultato:

TopicName = Maps 
TopicDescription = Methods in Maps 
TopicName maps to: Maps 
TopicDescription maps to: Methods in Maps

Spesso, potremmo voler iterare tra i membri di una raccolta e applicare una logica solo quando l'elemento soddisfa un criterio. Questo è prontamente gestito con una dichiarazione condizionale nella chiusura.

class Example {
   static void main(String[] args) {
      def lst = [1,2,3,4];
      lst.each {println it}
      println("The list will only display those numbers which are divisible by 2")
      lst.each{num -> if(num % 2 == 0) println num}
   } 
}

L'esempio precedente mostra l'espressione condizionale if (num% 2 == 0) utilizzata nella chiusura che viene utilizzata per verificare se ogni elemento nell'elenco è divisibile per 2.

Quando eseguiamo il programma sopra, otterremo il seguente risultato:

1 
2 
3 
4 
The list will only display those numbers which are divisible by 2.
2 
4

Metodi utilizzati con le chiusure

Le stesse chiusure forniscono alcuni metodi.

Sr.No. Metodi e descrizione
1 trova()

Il metodo find trova il primo valore in una raccolta che corrisponde a un criterio.

2 trova tutto()

Trova tutti i valori nell'oggetto ricevente che corrispondono alla condizione di chiusura.

3 any () & every ()

Il metodo any esegue l'iterazione di ogni elemento di una raccolta verificando se un predicato booleano è valido per almeno un elemento.

4 raccogliere()

Il metodo collect itera attraverso una raccolta, convertendo ogni elemento in un nuovo valore utilizzando la chiusura come trasformatore.