Penutupan di Groovy

1. Ikhtisar

Dalam tutorial Pengenalan ini, kita akan menjelajahi konsep closure di Groovy, fitur utama dari bahasa JVM yang dinamis dan kuat ini.

Banyak bahasa lain, termasuk Javascript dan Python, mendukung konsep closure. Namun, karakteristik dan fungsi closure bervariasi dari satu bahasa ke bahasa lainnya.

Kami akan menyentuh aspek kunci dari penutupan Groovy, menunjukkan contoh bagaimana mereka digunakan di sepanjang jalan.

2. Apa Itu Penutupan?

Penutupan adalah blok kode anonim. Di Groovy, ini adalah turunan dari kelas Penutupan . Penutupan dapat mengambil 0 atau lebih parameter dan selalu mengembalikan nilai.

Selain itu, closure dapat mengakses variabel di sekitarnya di luar cakupannya dan menggunakannya - bersama dengan variabel lokalnya - selama eksekusi.

Selanjutnya, kita dapat menetapkan closure ke variabel atau meneruskannya sebagai parameter ke metode. Oleh karena itu, closure menyediakan fungsionalitas untuk eksekusi tertunda.

3. Deklarasi Penutupan

A Groovy Closure berisi parameter, panah ->, dan kode untuk dieksekusi. Parameter bersifat opsional dan, jika disediakan, dipisahkan dengan koma.

3.1. Deklarasi Dasar

def printWelcome = { println "Welcome to Closures!" }

Di sini, closure printWelcome mencetak pernyataan saat dipanggil. Sekarang, mari kita tulis contoh singkat dari penutupan unary:

def print = { name -> println name }

Di sini, cetakan penutupan mengambil satu parameter - nama - dan mencetaknya saat dipanggil.

Karena definisi closure terlihat mirip dengan sebuah metode , mari kita bandingkan mereka:

def formatToLowerCase(name) { return name.toLowerCase() } def formatToLowerCaseClosure = { name -> return name.toLowerCase() } 

Di sini, metode dan closure yang sesuai berperilaku serupa. Namun, ada perbedaan halus antara closure dan metode, yang akan kita diskusikan nanti di bagian Closures vs Methods.

3.2. Eksekusi

Kita bisa mengeksekusi closure dengan dua cara - kita bisa memanggilnya seperti metode lain, atau kita bisa menggunakan metode panggilan .

Misalnya, sebagai metode biasa:

print("Hello! Closure") formatToLowerCaseClosure("Hello! Closure") 

Dan mengeksekusi dengan metode panggilan :

print.call("Hello! Closure") formatToLowerCaseClosure.call("Hello! Closure")

4. Parameter

Parameter penutupan Groovy mirip dengan metode biasa.

4.1. Parameter Implisit

Kita dapat mendefinisikan penutupan unary tanpa parameter karena ketika parameter tidak ditentukan, Groovy mengasumsikan parameter implisit bernama " it" :

def greet = { return "Hello! ${it}" } assert greet("Alex") == "Hello! Alex"

4.2. Beberapa Parameter

Berikut penutupan yang mengambil dua parameter dan mengembalikan hasil perkaliannya:

def multiply = { x, y -> return x*y } assert multiply(2, 4) == 8

4.3. Jenis Parameter

Dalam contoh sejauh ini, belum ada tipe yang diberikan dengan parameter kita. Kami juga dapat mengatur jenis parameter penutupan. Misalnya, mari kita tulis ulang metode perkalian untuk mempertimbangkan operasi lain:

def calculate = {int x, int y, String operation -> def result = 0 switch(operation) { case "ADD": result = x+y break case "SUB": result = x-y break case "MUL": result = x*y break case "DIV": result = x/y break } return result } assert calculate(12, 4, "ADD") == 16 assert calculate(43, 8, "DIV") == 5.375

4.4. Varargs

Kita bisa mendeklarasikan sejumlah variabel argumen dalam closure, mirip dengan metode biasa. Sebagai contoh:

def addAll = { int... args -> return args.sum() } assert addAll(12, 10, 14) == 36

5. Penutupan sebagai Argumen

Kita bisa melewatkan Closure sebagai argumen ke metode Groovy biasa. Ini memungkinkan metode memanggil closure kita untuk menyelesaikan tugasnya, memungkinkan kita menyesuaikan perilakunya.

Mari kita bahas kasus penggunaan sederhana: perhitungan volume angka biasa.

Dalam contoh ini, volume didefinisikan sebagai luas dikalikan dengan tinggi. Namun, penghitungan luas dapat bervariasi untuk berbagai bentuk.

Oleh karena itu, kami akan menulis metode volume , yang menggunakan closure areaCalculator sebagai argumen, dan kami akan meneruskan implementasi penghitungan area selama pemanggilan:

def volume(Closure areaCalculator, int... dimensions) { if(dimensions.size() == 3) { return areaCalculator(dimensions[0], dimensions[1]) * dimensions[2] } else if(dimensions.size() == 2) { return areaCalculator(dimensions[0]) * dimensions[1] } else if(dimensions.size() == 1) { return areaCalculator(dimensions[0]) * dimensions[0] } } assert volume({ l, b -> return l*b }, 12, 6, 10) == 720 

Mari kita temukan volume kerucut menggunakan metode yang sama:

assert volume({ radius -> return Math.PI*radius*radius/3 }, 5, 10) == Math.PI * 250

6. Penutupan Bersarang

Kita bisa mendeklarasikan dan meminta closure di dalam closure.

Misalnya, mari tambahkan kemampuan logging ke perhitungan closure yang sudah dibahas :

def calculate = {int x, int y, String operation -> def log = { println "Performing $it" } def result = 0 switch(operation) { case "ADD": log("Addition") result = x+y break case "SUB": log("Subtraction") result = x-y break case "MUL": log("Multiplication") result = x*y break case "DIV": log("Division") result = x/y break } return result }

7. Evaluasi Lazy dari Strings

Groovy String biasanya dievaluasi dan diinterpolasi pada saat pembuatan. Misalnya:

def name = "Samwell" def welcomeMsg = "Welcome! $name" assert welcomeMsg == "Welcome! Samwell"

Even if we modify the value of the name variable, the welcomeMsg is not going to change:

name = "Tarly" assert welcomeMsg != "Welcome! Tarly"

Closure interpolation allows us to provide lazy evaluation of Strings, recalculated from the current values around them. For example:

def fullName = "Tarly Samson" def greetStr = "Hello! ${-> fullName}" assert greetStr == "Hello! Tarly Samson"

Only this time, changing the variable affects the interpolated string's value as well:

fullName = "Jon Smith" assert greetStr == "Hello! Jon Smith"

8. Closures in Collections

Groovy Collections use closures in many of their APIs. For example, let's define a list of items and print them using the unary closure each, which has an implicit parameter:

def list = [10, 11, 12, 13, 14, true, false, "BUNTHER"] list.each { println it } assert [13, 14] == list.findAll{ it instanceof Integer && it >= 13 }

Often, based on some criterion, we may need to create a list from a map. For instance:

def map = [1:10, 2:30, 4:5] assert [10, 60, 20] == map.collect{it.key * it.value} 

9. Closures vs Methods

So far, we've seen the syntax, execution, and parameters of closures, which are fairly similar to methods. Let's now compare closures with methods.

Unlike a regular Groovy method:

  • We can pass a Closure as an argument to a method
  • Unary closures can use the implicit it parameter
  • We can assign a Closure to a variable and execute it later, either as a method or with call
  • Groovy determines the return type of the closures at runtime
  • We can declare and invoke closures inside a closure
  • Closures always return a value

Hence, closures have benefits over regular methods and are a powerful feature of Groovy.

10. Conclusion

In this article, we’ve seen how to create closures in Groovy and explored how they are used.

Closure menyediakan cara yang efektif untuk memasukkan fungsionalitas ke dalam objek dan metode untuk eksekusi tertunda.

Seperti biasa, pengujian kode dan unit dari artikel ini tersedia di GitHub.