Groovy - Zamknięcia

Zamknięcie to krótki anonimowy blok kodu. Zwykle obejmuje kilka wierszy kodu. Metoda może nawet przyjąć blok kodu jako parametr. Mają charakter anonimowy.

Poniżej znajduje się przykład prostego zamknięcia i jego wyglądu.

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

W powyższym przykładzie linia kodu - {println "Hello World"} jest znana jako zamknięcie. Blok kodu, do którego odwołuje się ten identyfikator, można wykonać za pomocą instrukcji call.

Po uruchomieniu powyższego programu otrzymamy następujący wynik -

Hello World

Parametry formalne w domknięciach

Zamknięcia mogą również zawierać parametry formalne, aby były bardziej przydatne, podobnie jak metody w Groovy.

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

W powyższym przykładzie kodu zwróć uwagę na użycie $ {param}, które powoduje, że zamknięcie przyjmuje parametr. Wywołując zamknięcie za pomocą instrukcji clos.call, mamy teraz możliwość przekazania parametru do zamknięcia.

Po uruchomieniu powyższego programu otrzymamy następujący wynik -

Hello World

Następna ilustracja powtarza poprzedni przykład i daje ten sam wynik, ale pokazuje, że niejawny pojedynczy parametr, o którym mowa, może być użyty. Tutaj „to” jest słowem kluczowym w Groovy.

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

Po uruchomieniu powyższego programu otrzymamy następujący wynik -

Hello World

Zamknięcia i zmienne

Bardziej formalnie, zamknięcia mogą odnosić się do zmiennych w czasie definiowania zamknięcia. Poniżej znajduje się przykład tego, jak można to osiągnąć.

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

W powyższym przykładzie, oprócz przekazania parametru do zamknięcia, definiujemy również zmienną o nazwie str1. Zamknięcie przejmuje również zmienną wraz z parametrem.

Po uruchomieniu powyższego programu otrzymamy następujący wynik -

Hello World 
Welcome World

Stosowanie domknięć w metodach

Zamknięcia mogą być również używane jako parametry metod. W Groovy wiele wbudowanych metod dla typów danych, takich jak listy i kolekcje, ma zamknięcia jako typ parametru.

Poniższy przykład pokazuje, jak zamknięcie można wysłać do metody jako parametr.

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

W powyższym przykładzie

  • Definiujemy statyczną metodę o nazwie Display, która przyjmuje zamknięcie jako argument.

  • Następnie definiujemy zamknięcie w naszej głównej metodzie i przekazujemy je do naszej metody Display jako parametr.

Po uruchomieniu powyższego programu otrzymamy następujący wynik -

Hello World 
Welcome World 
Welcome Inner

Zamknięcia w kolekcjach i ciągach

Kilka metod List, Map i String akceptuje zamknięcie jako argument. Spójrzmy na przykład, jak domknięcia mogą być używane w tych typach danych.

Używanie domknięć z listami

Poniższy przykład pokazuje, jak domknięcia mogą być używane z listami. W poniższym przykładzie najpierw definiujemy prostą listę wartości. Typ zbioru list definiuje następnie funkcję o nazwie.each. Ta funkcja przyjmuje zamknięcie jako parametr i stosuje zamknięcie do każdego elementu listy.

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

Po uruchomieniu powyższego programu otrzymamy następujący wynik -

11 
12 
13 
14

Używanie zamknięć z mapami

Poniższy przykład pokazuje, jak zamknięcia mogą być używane w Mapach. W poniższym przykładzie najpierw definiujemy prostą mapę kluczowych elementów wartości. Typ zbioru map definiuje następnie funkcję o nazwie .each. Ta funkcja przyjmuje zamknięcie jako parametr i stosuje zamknięcie do każdej pary klucz-wartość mapy.

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

Po uruchomieniu powyższego programu otrzymamy następujący wynik -

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

Często możemy chcieć przeprowadzić iterację w obrębie elementów kolekcji i zastosować jakąś logikę tylko wtedy, gdy element spełnia jakieś kryterium. Można to łatwo załatwić za pomocą instrukcji warunkowej w zamknięciu.

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

Powyższy przykład pokazuje warunkowe wyrażenie if (num% 2 == 0) używane w zamknięciu, które służy do sprawdzenia, czy każda pozycja na liście jest podzielna przez 2.

Po uruchomieniu powyższego programu otrzymamy następujący wynik -

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

Metody używane z zamknięciami

Same zamknięcia dostarczają pewnych metod.

Sr.No. Metody i opis
1 odnaleźć()

Metoda find znajduje pierwszą wartość w kolekcji, która pasuje do jakiegoś kryterium.

2 Znajdź wszystko()

Znajduje wszystkie wartości w obiekcie odbierającym pasujące do warunku zamknięcia.

3 dowolne () i co ()

Metoda any wykonuje iterację w każdym elemencie kolekcji, sprawdzając, czy predykat boolowski jest prawidłowy dla co najmniej jednego elementu.

4 zbierać()

Metoda collect wykonuje iterację w kolekcji, konwertując każdy element na nową wartość, używając zamknięcia jako transformatora.