Penutupan di Groovy

1. Gambaran keseluruhan

Dalam tutorial Pengenalan ini, kita akan meneroka konsep penutupan di Groovy, ciri utama bahasa JVM yang dinamik dan kuat ini.

Banyak bahasa lain, termasuk Javascript dan Python, menyokong konsep penutupan. Walau bagaimanapun, ciri dan fungsi penutupan berbeza dari bahasa ke bahasa.

Kami akan menyentuh aspek utama penutupan Groovy, menunjukkan contoh bagaimana ia digunakan sepanjang perjalanan.

2. Apakah Penutupan itu?

Penutupan adalah sekatan kod tanpa nama. Di Groovy, ini adalah contoh kelas Penutupan . Penutupan boleh mengambil 0 atau lebih parameter dan selalu mengembalikan nilai.

Sebagai tambahan, penutupan boleh mengakses pemboleh ubah di sekitarnya dan menggunakannya - bersama dengan pemboleh ubah tempatan - semasa pelaksanaan.

Selanjutnya, kita dapat menetapkan penutupan ke pemboleh ubah atau meneruskannya sebagai parameter ke suatu kaedah. Oleh itu, penutupan memberikan fungsi untuk pelaksanaan yang tertangguh.

3. Deklarasi Penutupan

Penutupan Groovy mengandungi parameter, anak panah ->, dan kod yang akan dilaksanakan. Parameter adalah pilihan dan, apabila disediakan, dipisahkan dengan koma.

3.1. Pengisytiharan Asas

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

Di sini, penutupan printWelcome mencetak pernyataan apabila dipanggil. Sekarang, mari kita tulis contoh ringkas penutupan yang tidak wajar:

def print = { name -> println name }

Di sini, cetakan penutup mengambil satu parameter - nama - dan mencetaknya apabila diminta.

Oleh kerana definisi penutupan kelihatan serupa dengan kaedah , mari bandingkan:

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

Di sini, kaedah dan penutupan yang bersangkutan bertindak sama. Walau bagaimanapun, terdapat perbezaan halus antara penutupan dan kaedah, yang akan kita bincangkan kemudian di bahagian Penutupan vs Kaedah.

3.2. Pelaksanaan

Kita boleh melakukan penutupan dengan dua cara - kita boleh memaksanya seperti kaedah lain, atau kita boleh menggunakan kaedah panggilan .

Sebagai contoh, sebagai kaedah biasa:

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

Dan melaksanakan dengan kaedah panggilan :

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

4. Parameter

Parameter penutupan Groovy serupa dengan kaedah biasa.

4.1. Parameter Tersirat

Kita dapat menentukan penutupan unary tanpa parameter kerana ketika parameter tidak ditentukan, Groovy menganggap parameter tersirat bernama " itu" :

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

4.2. Pelbagai Parameter

Berikut adalah penutupan yang mengambil dua parameter dan mengembalikan hasil mengalikannya:

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

4.3. Jenis Parameter

Dalam contoh setakat ini, tidak ada jenis yang disediakan dengan parameter kami. Kami juga dapat menetapkan jenis parameter penutupan. Sebagai contoh, mari tulis semula kaedah darab 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

Kami boleh menyatakan sejumlah argumen dalam penutupan, serupa dengan kaedah biasa. Sebagai contoh:

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

5. Penutupan Sebagai Hujah

Kita boleh menyampaikan Penutupan sebagai hujah kepada kaedah Groovy biasa. Ini membolehkan kaedah memanggil penutupan kita untuk menyelesaikan tugasnya, yang memungkinkan kita untuk menyesuaikan perilakunya.

Mari kita bincangkan kes penggunaan yang mudah: pengiraan jumlah angka biasa.

Dalam contoh ini, isipadu ditakrifkan sebagai luas yang didarab dengan tinggi. Walau bagaimanapun, pengiraan luas boleh berbeza untuk pelbagai bentuk.

Oleh itu, kami akan menulis kaedah isipadu , yang mengambil penutupan kawasan Kalkulator sebagai argumen, dan kami akan lulus pelaksanaan pengiraan kawasan semasa 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 cari isipadu kerucut menggunakan kaedah yang sama:

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

6. Penutupan Bersarang

Kita boleh menyatakan dan meminta penutupan di dalam penutupan.

Sebagai contoh, mari kita tambahkan kemampuan pembalakan ke penutupan pengiraan yang telah dibincangkan :

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. Penilaian Rentak Malas

Groovy String biasanya dinilai dan diinterpolasi pada masa penciptaan. Contohnya:

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.

Penutupan menyediakan cara yang berkesan untuk menyuntik fungsi ke objek dan kaedah untuk pelaksanaan yang tertunda.

Seperti biasa, ujian kod dan unit dari artikel ini terdapat di GitHub.