Groovy - Замыкания

Замыкание - это короткий анонимный блок кода. Обычно он занимает несколько строк кода. Метод может даже принимать блок кода в качестве параметра. Они анонимны по своей природе.

Ниже приведен пример простого закрытия и его внешний вид.

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

В приведенном выше примере строка кода - {println "Hello World"} известна как закрытие. Блок кода, на который ссылается этот идентификатор, может быть выполнен с помощью оператора call.

Когда мы запустим вышеуказанную программу, мы получим следующий результат -

Hello World

Формальные параметры в закрытии

Замыкания также могут содержать формальные параметры, чтобы сделать их более полезными, как и методы в Groovy.

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

В приведенном выше примере кода обратите внимание на использование $ {param}, которое заставляет замыкание принимать параметр. При вызове закрытия через оператор clos.call теперь у нас есть возможность передать параметр в закрытие.

Когда мы запустим вышеуказанную программу, мы получим следующий результат -

Hello World

Следующая иллюстрация повторяет предыдущий пример и дает тот же результат, но показывает, что можно использовать неявный единственный параметр, называемый им. Здесь «это» - ключевое слово в Groovy.

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

Когда мы запустим вышеуказанную программу, мы получим следующий результат -

Hello World

Замыкания и переменные

Более формально замыкания могут относиться к переменным во время определения замыкания. Ниже приводится пример того, как этого можно достичь.

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

В приведенном выше примере, помимо передачи параметра в замыкание, мы также определяем переменную с именем str1. Замыкание также принимает переменную вместе с параметром.

Когда мы запустим вышеуказанную программу, мы получим следующий результат -

Hello World 
Welcome World

Использование замыканий в методах

Замыкания также можно использовать как параметры методов. В Groovy многие встроенные методы для типов данных, таких как списки и коллекции, имеют замыкания в качестве типа параметра.

В следующем примере показано, как замыкание может быть отправлено методу в качестве параметра.

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

В приведенном выше примере

  • Мы определяем статический метод Display, который принимает закрытие в качестве аргумента.

  • Затем мы определяем замыкание в нашем основном методе и передаем его нашему методу Display в качестве параметра.

Когда мы запустим вышеуказанную программу, мы получим следующий результат -

Hello World 
Welcome World 
Welcome Inner

Замыкания в коллекциях и строке

Некоторые методы List, Map и String принимают закрытие в качестве аргумента. Давайте посмотрим на пример того, как замыкания можно использовать в этих типах данных.

Использование замыканий со списками

В следующем примере показано, как замыкания можно использовать со списками. В следующем примере мы сначала определяем простой список значений. Затем тип коллекции списка определяет вызываемую функцию.each. Эта функция принимает закрытие в качестве параметра и применяет закрытие к каждому элементу списка.

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

Когда мы запустим вышеуказанную программу, мы получим следующий результат -

11 
12 
13 
14

Использование замыканий с картами

В следующем примере показано, как замыкания можно использовать с Maps. В следующем примере мы сначала определяем простую карту элементов значения ключа. Затем тип коллекции карт определяет функцию с именем .each. Эта функция принимает закрытие в качестве параметра и применяет закрытие к каждой паре "ключ-значение" карты.

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

Когда мы запустим вышеуказанную программу, мы получим следующий результат -

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

Часто мы можем захотеть перебрать элементы коллекции и применить некоторую логику только тогда, когда элемент соответствует определенному критерию. Это легко обрабатывается с помощью условного оператора в замыкании.

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

В приведенном выше примере показано условное выражение if (num% 2 == 0), используемое в закрытии, которое используется для проверки, делится ли каждый элемент в списке на 2.

Когда мы запустим вышеуказанную программу, мы получим следующий результат -

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

Методы, используемые с замыканиями

Сами замыкания предоставляют некоторые методы.

Sr. No. Методы и описание
1 найти()

Метод find находит первое значение в коллекции, которое соответствует некоторому критерию.

2 найти все()

Он находит все значения в принимающем объекте, соответствующие условию закрытия.

3 любой () и каждый ()

Метод any выполняет итерацию по каждому элементу коллекции, проверяя, действителен ли логический предикат хотя бы для одного элемента.

4 собирать ()

Метод collect выполняет итерацию по коллекции, преобразовывая каждый элемент в новое значение, используя замыкание в качестве преобразователя.