Groovy - Cierres

Un cierre es un breve bloque de código anónimo. Normalmente solo abarca unas pocas líneas de código. Un método puede incluso tomar el bloque de código como parámetro. Son de naturaleza anónima.

A continuación, se muestra un ejemplo de un cierre simple y su apariencia.

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

En el ejemplo anterior, la línea de código - {println "Hello World"} se conoce como cierre. El bloque de código al que hace referencia este identificador se puede ejecutar con la declaración de llamada.

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado:

Hello World

Parámetros formales en cierres

Los cierres también pueden contener parámetros formales para hacerlos más útiles al igual que los métodos en Groovy.

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

En el ejemplo de código anterior, observe el uso de $ {param} que hace que el cierre tome un parámetro. Al llamar al cierre a través de la sentencia clos.call, ahora tenemos la opción de pasar un parámetro al cierre.

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado:

Hello World

La siguiente ilustración repite el ejemplo anterior y produce el mismo resultado, pero muestra que se puede utilizar un único parámetro implícito al que se hace referencia como él. Aquí 'eso' es una palabra clave en Groovy.

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

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado:

Hello World

Cierres y Variables

Más formalmente, los cierres pueden referirse a variables en el momento en que se define el cierre. A continuación se muestra un ejemplo de cómo se puede lograr esto.

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");
   } 
}

En el ejemplo anterior, además de pasar un parámetro al cierre, también estamos definiendo una variable llamada str1. El cierre también toma la variable junto con el parámetro.

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado:

Hello World 
Welcome World

Uso de cierres en métodos

Los cierres también se pueden utilizar como parámetros de métodos. En Groovy, muchos de los métodos incorporados para tipos de datos como Listas y colecciones tienen cierres como tipo de parámetro.

El siguiente ejemplo muestra cómo se puede enviar un cierre a un método como parámetro.

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);
   } 
}

En el ejemplo anterior,

  • Estamos definiendo un método estático llamado Display que toma un cierre como argumento.

  • Luego estamos definiendo un cierre en nuestro método principal y pasándolo a nuestro método Display como parámetro.

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado:

Hello World 
Welcome World 
Welcome Inner

Cierres en colecciones y cadena

Varios métodos List, Map y String aceptan un cierre como argumento. Veamos un ejemplo de cómo se pueden usar los cierres en estos tipos de datos.

Usar cierres con listas

El siguiente ejemplo muestra cómo se pueden utilizar los cierres con listas. En el siguiente ejemplo, primero definimos una lista simple de valores. El tipo de colección de lista luego define una función llamada.each. Esta función toma un cierre como parámetro y aplica el cierre a cada elemento de la lista.

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

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado:

11 
12 
13 
14

Usar cierres con mapas

El siguiente ejemplo muestra cómo se pueden utilizar los cierres con Maps. En el siguiente ejemplo, primero definimos un mapa simple de elementos de valor clave. El tipo de colección de mapas luego define una función llamada .each. Esta función toma un cierre como parámetro y aplica el cierre a cada par clave-valor del mapa.

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}"}
   } 
}

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado:

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

A menudo, es posible que deseemos iterar a través de los miembros de una colección y aplicar alguna lógica solo cuando el elemento cumple con algún criterio. Esto se maneja fácilmente con una declaración condicional en el cierre.

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}
   } 
}

El ejemplo anterior muestra la expresión condicional if (num% 2 == 0) que se usa en el cierre, que se usa para verificar si cada elemento de la lista es divisible por 2.

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado:

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

Métodos utilizados con cierres

Los cierres en sí proporcionan algunos métodos.

No Señor. Métodos y descripción
1 encontrar()

El método de búsqueda encuentra el primer valor de una colección que coincide con algún criterio.

2 encuentra todos()

Encuentra todos los valores en el objeto receptor que coinciden con la condición de cierre.

3 cualquiera () y cada ()

El método any recorre cada elemento de una colección y comprueba si un predicado booleano es válido para al menos un elemento.

4 recoger()

El método recolectar itera a través de una colección, convirtiendo cada elemento en un nuevo valor usando el cierre como transformador.