Sluitingen in Groovy

1. Overzicht

In deze inleidende tutorial verkennen we het concept van sluitingen in Groovy, een belangrijk kenmerk van deze dynamische en krachtige JVM-taal.

Veel andere talen, waaronder Javascript en Python, ondersteunen het concept van sluitingen. De kenmerken en werking van sluitingen verschillen echter van taal tot taal.

We zullen de belangrijkste aspecten van Groovy-sluitingen bespreken en voorbeelden laten zien van hoe ze onderweg worden gebruikt.

2. Wat is een sluiting?

Een sluiting is een anoniem codeblok. In Groovy is het een instantie van de Sluiting klasse. Sluitingen kunnen 0 of meer parameters aannemen en altijd een waarde retourneren.

Bovendien kan een afsluiting toegang krijgen tot omgevingsvariabelen buiten zijn bereik en deze - samen met zijn lokale variabelen - gebruiken tijdens de uitvoering.

Bovendien kunnen we een afsluiting aan een variabele toewijzen of deze als parameter doorgeven aan een methode. Daarom biedt een sluiting functionaliteit voor vertraagde uitvoering.

3. Afsluitingsverklaring

Een Groovy Closure bevat parameters, de pijl -> en de code die moet worden uitgevoerd. Parameters zijn optioneel en worden, indien opgegeven, door komma's gescheiden.

3.1. Basisverklaring

def printWelcome = {println "Welkom bij sluitingen!" }

Hier de sluiting printWelkom drukt een verklaring af wanneer deze wordt aangeroepen. Laten we nu een kort voorbeeld geven van een unaire sluiting:

def print = {naam -> println naam}

Hier de sluiting afdrukken neemt één parameter - naam - en drukt het af wanneer het wordt aangeroepen.

Omdat de definitie van een sluiting lijkt op een methode, laten we ze vergelijken:

def formatToLowerCase (naam) {retourneer naam.toLowerCase ()} def formatToLowerCaseCase = {naam -> retourneer naam.toLowerCase ()} 

Hier gedragen de methode en de bijbehorende sluiting zich op dezelfde manier. Er zijn echter subtiele verschillen tussen een sluiting en een methode, die we later zullen bespreken in de sectie Afsluitingen versus methoden.

3.2. Executie

We kunnen een afsluiting op twee manieren uitvoeren - we kunnen het oproepen alsof het elke andere methode is, of we kunnen de bellen methode.

Bijvoorbeeld als reguliere methode:

print ("Hallo! Sluiting") formatToLowerCaseClosure ("Hallo! Sluiting") 

En uitvoeren met de bellen methode:

print.call ("Hallo! Afsluiting") formatToLowerCaseClosure.call ("Hallo! Afsluiting")

4. Parameters

De parameters van Groovy-sluitingen zijn vergelijkbaar met die van reguliere methoden.

4.1. Impliciete parameter

We kunnen een unaire sluiting definiëren zonder een parameter omdat als er geen parameters zijn gedefinieerd, gaat Groovy uit van een impliciete parameter met de naam 'het":

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

4.2. Meerdere parameters

Hier is een afsluiting die twee parameters nodig heeft en het resultaat van vermenigvuldiging retourneert:

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

4.3. Parametertypen

In de voorbeelden is er tot dusver geen type voorzien van onze parameters. We kunnen ook het type sluitingsparameters instellen. Laten we bijvoorbeeld het vermenigvuldigen methode om andere bewerkingen te overwegen:

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

4.4. Varargs

We kunnen een variabel aantal argumenten in sluitingen declareren, vergelijkbaar met reguliere methoden. Bijvoorbeeld:

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

5. Een afsluiting als argument

We kunnen een Sluiting als argument voor een reguliere Groovy-methode. Hierdoor kan de methode onze sluiting oproepen om zijn taak te voltooien, waardoor we zijn gedrag kunnen aanpassen.

Laten we een simpele use-case bespreken: de berekening van het volume van reguliere cijfers.

In dit voorbeeld wordt het volume gedefinieerd als oppervlakte vermenigvuldigd met hoogte. De berekening van de oppervlakte kan echter variëren voor verschillende vormen.

Daarom zullen we de volume methode, die wordt afgesloten areaCalculator als argument, en we zullen de implementatie van de gebiedsberekening tijdens het aanroepen doorgeven:

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

Laten we met dezelfde methode een volume van een kegel zoeken:

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

6. Geneste sluitingen

We kunnen sluitingen binnen een sluiting aangeven en inroepen.

Laten we bijvoorbeeld een logboekfunctie toevoegen aan het reeds besproken berekenen sluiting:

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

7. Luie evaluatie van snaren

Groovy Draads worden gewoonlijk geëvalueerd en geïnterpoleerd op het moment van creatie. Bijvoorbeeld:

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

Zelfs als we de waarde van de naam variabele, de welkom gaat niet veranderen:

name = "Tarly" assert welcomeMsg! = "Welkom! Tarly"

Door interpolatie van de sluiting kunnen we een luie evaluatie van Draads, herberekend op basis van de huidige waarden eromheen. Bijvoorbeeld:

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

Alleen deze keer heeft het wijzigen van de variabele ook invloed op de waarde van de geïnterpoleerde tekenreeks:

fullName = "Jon Smith" beweren greetStr == "Hallo! Jon Smith"

8. Sluitingen in collecties

Groovy Collections gebruikt sluitingen in veel van hun API's. Laten we bijvoorbeeld een lijst met items definiëren en deze afdrukken met de unaire sluiting elk, die een impliciete parameter heeft:

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

Vaak moeten we op basis van een of ander criterium een ‚Äč‚Äčlijst van een kaart maken. Bijvoorbeeld:

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

9. Afsluitingen versus methoden

Tot nu toe hebben we de syntaxis, uitvoering en parameters van sluitingen gezien, die redelijk vergelijkbaar zijn met methoden. Laten we nu sluitingen vergelijken met methoden.

In tegenstelling tot een gewone Groovy-methode:

  • We kunnen een Sluiting als argument voor een methode
  • Bij unaire sluitingen kan gebruik worden gemaakt van het impliciete het parameter
  • We kunnen een Sluiting naar een variabele en voer deze later uit, hetzij als een methode, hetzij met bellen
  • Groovy bepaalt het retourtype van de sluitingen tijdens runtime
  • We kunnen sluitingen binnen een sluiting aangeven en inroepen
  • Sluitingen geven altijd een waarde terug

Daarom hebben sluitingen voordelen ten opzichte van reguliere methoden en zijn ze een krachtig kenmerk van Groovy.

10. Conclusie

In dit artikel hebben we gezien hoe we sluitingen kunnen maken in Groovy en hebben we onderzocht hoe ze worden gebruikt.

Sluitingen bieden een effectieve manier om functionaliteit in objecten te injecteren en methoden voor vertraagde uitvoering.

Zoals altijd zijn de code- en unit-tests uit dit artikel beschikbaar op GitHub.