Metaprogrammering i Groovy
1. Oversigt
Groovy er et dynamisk og kraftfuldt JVM-sprog, der har adskillige funktioner som lukninger og træk.
I denne vejledning undersøger vi begrebet metaprogramming i Groovy.
2. Hvad er metaprogrammering?
Metaprogramming er en programmeringsteknik til at skrive et program for at ændre sig selv eller et andet program ved hjælp af metadata.
I Groovy er det muligt at udføre metaprogrammering på både runtime og compile-time. Fremadrettet vil vi udforske et par bemærkelsesværdige funktioner i begge teknikker.
3. Metaprogrammering under kørsel
Runtime-metaprogrammering giver os mulighed for at ændre de eksisterende egenskaber og metoder i en klasse. Vi kan også vedhæfte nye egenskaber og metoder; alt sammen ved kørsel.
Groovy giver et par metoder og egenskaber, der hjælper med at ændre en klasses opførsel ved kørsel.
3.1. propertyMissing
Når vi forsøger at få adgang til en udefineret ejendom i en Groovy-klasse, kaster den en Mangler ejendomsundtagelse. For at undgå undtagelsen leverer Groovy propertyMissing metode.
Lad os først skrive en Medarbejder klasse med nogle egenskaber:
klasse Medarbejder {String firstName String lastName int age}
For det andet opretter vi en Medarbejder objekt og prøv at vise en udefineret egenskab adresse. Derfor vil det smide Mangler ejendomsundtagelse: Groovy leverer propertyMissing metode til at fange den manglende ejendomsanmodning. Derfor kan vi undgå en Mangler ejendomsundtagelse ved kørselstid. For at fange en manglende ejendoms getter-metodeopkald definerer vi det med et enkelt argument for ejendomsnavnet: Den samme metode kan også have det andet argument som ejendommens værdi for at fange en manglende egenskabs settermetodeopkald: Det metodeMissing metoden ligner propertyMissing. Imidlertid, metodeMissing aflytter et kald til enhver manglende metode og derved undgår MissingMethodException. Lad os prøve at kalde getFullName metode på en Medarbejder objekt. Som getFullName mangler, vil udførelse kaste MissingMethodException ved kørselstid: Så i stedet for at indpakke en metodeopkald i en prøve-fangst, kan vi definere metodeMissing: Groovy giver en metaklasse ejendom i alle sine klasser. Det metaklasse ejendom refererer til en forekomst af ExpandoMetaClass. Det ExpandoMetaClass klasse giver adskillige måder at transformere en eksisterende klasse på runtime. For eksempel kan vi tilføje egenskaber, metoder eller konstruktører. Lad os først tilføje de manglende adresse ejendom til Medarbejder klasse ved hjælp af metaklasse ejendom: Vi bevæger os videre, lad os tilføje de manglende getFullName metode til Medarbejder klasseobjekt ved kørselstid: På samme måde kan vi tilføje en konstruktør til Medarbejder klasse ved runtime: Ligeledes kan vi tilføje statisk metoder ved hjælp af metaClass.static. Det metaklasse egenskab er ikke kun praktisk til at ændre brugerdefinerede klasser, men også eksisterende Java-klasser ved kørsel. Lad os f.eks. Tilføje en kapitalisere metode til Snor klasse: En udvidelse kan tilføje en metode til en klasse ved kørsel og gøre den tilgængelig globalt. Metoderne defineret i en udvidelse skal altid være statiske med selv klasseobjekt som det første argument. Lad os for eksempel skrive en BasicExtension klasse for at tilføje en getYearOfBirth metode til Medarbejder klasse: For at aktivere BasicExtensions, bliver vi nødt til at tilføje konfigurationsfilen i META-INF / tjenester katalog over vores projekt. Så lad os tilføje org.codehaus.groovy.runtime.ExtensionModule fil med følgende konfiguration: Lad os kontrollere getYearOfBirth metode tilføjet i Medarbejder klasse: Tilsvarende at tilføje statisk metoder i en klasse, skal vi definere en separat udvidelsesklasse. Lad os f.eks. Tilføje en statisk metode getDefaultObj til vores Medarbejder klasse ved at definere Statisk medarbejderudvidelse klasse: Derefter aktiverer vi Statisk medarbejderudvidelse ved at tilføje følgende konfiguration til ExtensionModule fil: Nu er alt, hvad vi har brug for, at teste vores statiskgetDefaultObj metode til Medarbejder klasse: Tilsvarende ved hjælp af udvidelser kan vi tilføje en metode til præ-kompilerede Java-klasser synes godt om Heltal og Lang: Ved hjælp af specifikke kommentarer kan vi uden besvær ændre klassestrukturen på kompileringstidspunktet. Med andre ord, vi kan bruge kommentarer til at ændre klassens abstrakte syntaks-træ ved kompileringen. Lad os diskutere nogle af kommentarerne, som er ret praktiske i Groovy for at reducere kedelpladekoden. Mange af dem er tilgængelige i groovy.transform pakke. Hvis vi omhyggeligt analyserer, vil vi indse, at et par kommentarer indeholder funktioner, der ligner Java's Project Lombok. Det @ToString annotation tilføjer en standardimplementering af toString metode til en klasse ved kompileringstid. Alt, hvad vi har brug for, er at tilføje kommentaren til klassen. Lad os f.eks. Tilføje @ToString kommentar til vores Medarbejder klasse: Nu opretter vi et objekt af Medarbejder klasse og kontrollere den streng, der returneres af toString metode: Vi kan også erklære parametre som f.eks udelukker, inkluderer, inkludererPakke og ignorere Nulls med @ToString for at ændre outputstrengen. Lad os for eksempel udelukke id og pakke fra strengen i medarbejderobjektet: Brug @TupleConstructor i Groovy for at tilføje en parametreret konstruktør i klassen. Denne kommentar opretter en konstruktør med en parameter for hver egenskab. Lad os f.eks. Tilføje @TupleConstructor til Medarbejder klasse: Nu kan vi skabe Medarbejder objekt, der passerer parametre i rækkefølgen af egenskaber defineret i klassen. Hvis vi ikke leverer værdier til egenskaberne, mens vi opretter objekter, vil Groovy overveje standardværdier: Svarende til @ToString, kan vi erklære parametre som f.eks udelukker, inkluderer og includeSuperProperties med @TupleConstructor for at ændre adfærden hos den tilknyttede konstruktør efter behov. Vi kan bruge @EqualsAndHashCode for at generere standardimplementeringen af lige med og hashCode metoder på kompileringstidspunktet. Lad os kontrollere adfærden for @EqualsAndHashCode ved at tilføje det til Medarbejder klasse: @Canonical er en kombination af @ToString, @TupleConstructorog @EqualsAndHashCode kommentarer. Bare ved at tilføje det kan vi nemt inkludere alle tre i en Groovy-klasse. Vi kan også erklære @Canonical med en af de specifikke parametre for alle tre kommentarer. En hurtig og pålidelig måde at implementere på Klonabel interface er ved at tilføje @AutoClone kommentar. Lad os kontrollere klon metode efter tilføjelse @AutoClone til Medarbejder klasse: For at tilføje logsupport til enhver Groovy-klasse er alt, hvad vi har brug for, at tilføje kommentarer, der er tilgængelige i groovy.util.logging pakke. Lad os aktivere logning leveret af JDK ved at tilføje @Log kommentar til Medarbejder klasse. Derefter tilføjer vi logEmp metode: Ringer til logEmp metode på en Medarbejder objektet viser logfilerne på konsollen: Tilsvarende er @Commons kommentar er tilgængelig for at tilføje Apache Commons logning support. @ Log4j er tilgængelig til Apache Log4j 1.x logning support og @ Log4j2 til Apache Log4j 2.x. Endelig skal du bruge @ Slf4j for at tilføje Simple Logging Facade til Java-support. I denne vejledning har vi udforsket begrebet metaprogrammering i Groovy. Undervejs har vi set et par bemærkelsesværdige metaprogrammeringsfunktioner både til runtime og kompileringstid. På samme tid har vi undersøgt yderligere praktiske kommentarer, der er tilgængelige i Groovy for renere og dynamisk kode. Som normalt er kodeimplementeringerne til denne artikel tilgængelige på GitHub.Medarbejder emp = ny medarbejder (fornavn: "Norman", efternavn: "Lewis") println emp.address
groovy.lang.MissingPropertyException: Ingen sådan ejendom: adresse for klasse: com.baeldung.metaprogramming.Employee
def propertyMissing (String propertyName) {"property '$ propertyName' er ikke tilgængelig"}
hævde emp.address == "ejendomsadresse" er ikke tilgængelig "
def propertyMissing (String propertyName, propertyValue) {println "kan ikke indstille $ propertyValue - ejendom '$ propertyName' er ikke tilgængelig"}
3.2. metodeMissing
prøv {emp.getFullName ()} fangst (MissingMethodException e) {println "metoden er ikke defineret"}
def methodMissing (String methodName, def methodArgs) {"method '$ methodName' er ikke defineret"}
hævde emp.getFullName () == "metode 'getFullName' er ikke defineret"
3.3. ExpandoMetaClass
Employee.metaClass.address = ""
Medarbejder emp = ny medarbejder (fornavn: "Norman", efternavn: "Lewis", adresse: "US") hævder emp.address == "US"
emp.metaClass.getFullName = {"$ lastName, $ firstName"}
hævde emp.getFullName () == "Lewis, Norman"
Employee.metaClass.constructor = {String firstName -> ny medarbejder (firstName: firstName)}
Medarbejder norman = ny medarbejder ("Norman") hævder norman.firstName == "Norman" hævder norman.lastName == null
String.metaClass.capitalize = {String str -> str.substring (0, 1) .toUpperCase () + str.substring (1)}
hævder "norman" .capitalize () == "Norman"
3.4. Udvidelser
klasse BasicExtensions {static int getYearOfBirth (Medarbejder selv) {return Year.now (). værdi - self.age}}
moduleName = core-groovy-2 moduleVersion = 1.0-SNAPSHOT extensionClasses = com.baeldung.metaprogramming.extension.BasicExtensions
def alder = 28 def forventetYearOfBirth = Year.now () - alder Medarbejder emp = ny medarbejder (alder: alder) hævder emp.getYearOfBirth () == forventetYearOfBirth.value
klasse StaticEmployeeExtension {static Employee getDefaultObj (Employee self) {return new Employee (firstName: "firstName", lastName: "lastName", age: 20)}}
staticExtensionClasses = com.baeldung.metaprogramming.extension.StaticEmployeeExtension
hævde Employee.getDefaultObj (). firstName == "firstName" hævde Employee.getDefaultObj (). lastName == "lastName" hævde Employee.getDefaultObj (). alder == 20
offentlig statisk ugyldig printCounter (heltal selv) {mens (selv> 0) {println selv selv -} returner selv} hævder 5.printCounter () == 0
offentlig statisk Lang firkant (langt selv) {returner selv * selv} hævder 40l.square () == 1600l
4. Kompileringstidsmetaprogrammering
4.1. @ToString
@ToString klasse Medarbejder {lang id Streng fornavn Streng efternavn int alder}
Medarbejdermedarbejder = ny medarbejder () medarbejder.id = 1 medarbejder.firstName = "norman" medarbejder.lastnavn = "lewis" medarbejder.alder = 28 hævder medarbejder.toString () == "com.baeldung.metaprogramming.Employee (1, Norman, Lewis, 28) "
@ToString (includePackage = false, ekskluderer = ['id'])
hævde medarbejder.toString () == "Medarbejder (norman, lewis, 28)"
4.2. @TupleConstructor
@TupleConstructor klasse Medarbejder {lang id Streng fornavn Streng efternavn int alder}
Medarbejder norman = ny medarbejder (1, "norman", "lewis", 28) hævder norman.toString () == "Medarbejder (norman, lewis, 28)"
Medarbejder snape = ny medarbejder (2, "snape") hævder snape.toString () == "Medarbejder (snape, null, 0)"
4.3. @EqualsAndHashCode
Medarbejder normanCopy = ny medarbejder (1, "norman", "lewis", 28) hævder norman == normanCopy hævder norman.hashCode () == normanCopy.hashCode ()
4.4. @Canonical
4.5. @AutoClone
prøv {Medarbejder norman = ny medarbejder (1, "norman", "lewis", 28) def normanCopy = norman.clone () hævder norman == normanCopy} fangst (CloneNotSupportedException e) {e.printStackTrace ()}
4.6. Logningsstøtte med @Log, @Commons, @ Log4j, @ Log4j2, og @ Slf4j
def logEmp () {log.info "Medarbejder: $ lastName, $ firstName er på $ age år"}
Medarbejdermedarbejder = ny medarbejder (1, "Norman", "Lewis", 28) medarbejder.logEmp ()
INFO: Medarbejder: Lewis, Norman er 28 år gammel
5. Konklusion