Metaprogramovanie v Groovy

1. Prehľad

Groovy je dynamický a výkonný jazyk JVM, ktorý má množstvo funkcií, ako sú uzávery a vlastnosti.

V tomto tutoriáli preskúmame koncept metaprogramovania v Groovy.

2. Čo je metaprogramovanie?

Metaprogramovanie je programovacia technika písania programu na úpravu samotného alebo iného programu pomocou metadát.

V aplikácii Groovy je možné vykonávať metaprogramovanie za behu aj kompilácie. Do budúcnosti preskúmame niekoľko pozoruhodných vlastností oboch techník.

3. Metaprogramovanie za behu

Runtime metaprogramovanie nám umožňuje meniť existujúce vlastnosti a metódy triedy. Môžeme tiež pripojiť nové vlastnosti a metódy; všetko za behu.

Groovy poskytuje niekoľko metód a vlastností, ktoré pomáhajú meniť správanie triedy za behu.

3.1. majetok chýba

Keď sa pokúsime získať prístup k nedefinovanej vlastnosti triedy Groovy, hodí a MissingPropertyException. Aby sa zabránilo výnimke, Groovy poskytuje majetok chýba metóda.

Najprv napíšeme Zamestnanec trieda s niektorými vlastnosťami:

trieda Zamestnanec {String krstné meno String priezvisko int vek}

Po druhé, vytvoríme Zamestnanec objekt a pokúsiť sa zobraziť nedefinovanú vlastnosť adresa. Následne to vyhodí MissingPropertyException:

Zamestnanec emp = nový zamestnanec (meno: "Norman", priezvisko: "Lewis") println emp.address 
groovy.lang.MissingPropertyException: Žiadna takáto vlastnosť: adresa pre triedu: com.baeldung.metaprogramming.Employee

Groovy poskytuje majetok chýba metóda na zachytenie chýbajúcej žiadosti o vlastnosť. Preto sa môžeme vyhnúť a MissingPropertyException za behu.

Aby sme zachytili volanie getterovej metódy chýbajúcej vlastnosti, definujeme ju jediným argumentom pre názov vlastnosti:

def propertyMissing (reťazec propertyName) {"vlastnosť '$ propertyName' nie je k dispozícii"}
assert emp.address == "adresa 'nehnuteľnosti' nie je k dispozícii"

Rovnaká metóda môže mať aj druhý argument ako hodnotu vlastnosti, aby zachytila ​​volanie metódy setter chýbajúcej vlastnosti:

def propertyMissing (reťazec propertyName, propertyValue) {println "nie je možné nastaviť $ propertyValue - vlastnosť '$ propertyName' nie je k dispozícii"}

3.2. metóda chýbajú

The methodMissing metóda je podobná ako majetok chýba. Avšak metóda chýbajú zachytí výzvu na chýbajúcu metódu a vyhne sa MissingMethodException.

Skúsme zavolať getFullName metóda na Zamestnanec objekt. Ako getFullName chýba, poprava vyhodí MissingMethodException za behu:

skúste {emp.getFullName ()} chytiť (MissingMethodException e) {println "metóda nie je definovaná"}

Takže namiesto zabalenia volania metódy do a Skús chytiť, môžeme definovať methodMissing:

def methodMissing (String methodName, def methodArgs) {"metóda '$ methodName' nie je definovaná"}
assert emp.getFullName () == "metóda 'getFullName' nie je definovaná"

3.3. ExpandoMetaClass

Groovy poskytuje a metaClass majetku vo všetkých svojich triedach. The metaClass property odkazuje na inštanciu ExpandoMetaClass.

The ExpandoMetaClass trieda poskytuje množstvo spôsobov, ako transformovať existujúcu triedu za behu. Napríklad môžeme pridať vlastnosti, metódy alebo konštruktory.

Najskôr doplníme chýbajúce adresa majetok do Zamestnanec trieda pomocou metaClass nehnuteľnosť:

Employee.metaClass.address = ""
Zamestnanec emp = nový Zamestnanec (meno: "Norman", priezvisko: "Lewis", adresa: "US") assert emp.address == "US"

Ak sa posunieme ďalej, doplníme chýbajúce getFullName metóda do Zamestnanec objekt triedy za behu:

emp.metaClass.getFullName = {"$ priezvisko, $ krstne meno"}
assert emp.getFullName () == "Lewis, Norman"

Podobne môžeme pridať konštruktor do Zamestnanec trieda za behu:

Employee.metaClass.constructor = {Reťazec meno -> nový zamestnanec (meno: meno)}
Zamestnanec norman = nový Zamestnanec („Norman“) presadzuje norman.firstName == „Norman“ presadzuje norman.lastName == null

Rovnako tak môžeme pridať statický metódy využívajúce metaClass.static.

The metaClass Táto vlastnosť je nielen užitočná na modifikáciu užívateľom definovaných tried, ale aj existujúcich tried Java za behu programu.

Napríklad pridajme a kapitalizovať metóda do String trieda:

String.metaClass.capitalize = {String str -> str.substring (0, 1) .toUpperCase () + str.substring (1)}
tvrdiť „norman“ .capitalize () == „norman“

3.4. Prípony

Rozšírenie môže pridať metódu do triedy za behu a sprístupniť ju globálne.

Metódy definované v rozšírení by mali byť vždy statické, s ja objekt triedy ako prvý argument.

Napíšme napríklad a BasicExtension triedy pridať a getYearOfBirth metóda do Zamestnanec trieda:

trieda BasicExtensions {static int getYearOfBirth (zamestnanec samostatne) {návrat Year.now (). hodnota - self.age}}

Ak chcete povoliť BasicExtensions, budeme musieť pridať konfiguračný súbor do súboru META-INF / služby adresár nášho projektu.

Pridajme teda org.codehaus.groovy.runtime.ExtensionModule súbor s nasledujúcou konfiguráciou:

moduleName = core-groovy-2 moduleVersion = 1.0-SNAPSHOT extensionClasses = com.baeldung.metaprogramming.extension.BasicExtensions

Poďme si to overiť getYearOfBirth metóda pridaná do Zamestnanec trieda:

def vek = 28 def expectYearOfBirth = Year.now () - vek Zamestnanec emp = nový Zamestnanec (vek: vek) uplatniť emp.getYearOfBirth () == expectYearOfBirth.value

Podobne pridať statický metód v triede, budeme musieť definovať samostatnú triedu rozšírenia.

Napríklad pridajme a statický metóda getDefaultObj k nášmu Zamestnanec triedy definovaním StaticEmployeeExtension trieda:

trieda StaticEmployeeExtension {statický zamestnanec getDefaultObj (samostatne zamestnanec) {vrátiť nového zamestnanca (meno: "meno", priezvisko: "priezvisko", vek: 20)}}

Potom povolíme StaticEmployeeExtension pridaním nasledujúcej konfigurácie do ExtensionModule spis:

staticExtensionClasses = com.baeldung.metaprogramming.extension.StaticEmployeeExtension

Všetko, čo potrebujeme, je vyskúšať statickýgetDefaultObj metóda na Zamestnanec trieda:

assert Employee.getDefaultObj (). firstName == "firstName" assert Employee.getDefaultObj (). lastName == "lastName" assert Employee.getDefaultObj (). age == 20

Podobne pomocou rozšírení môžeme pridať metódu do predkompilovaných tried Java Páči sa mi to Celé číslo a Dlhé:

public static void printCounter (Integer self) {while (self> 0) {println self self--} return self} assert 5.printCounter () == 0 
public static Long square (Long self) {return self * self} assert 40l.square () == 1600l 

4. Metaprogramovanie v čase kompilácie

Pomocou konkrétnych anotácií môžeme bez námahy meniť štruktúru triedy v čase kompilácie. Inými slovami, môžeme použiť anotácie na úpravu abstraktného stromu syntaxe triedy pri kompilácii.

Poďme diskutovať o niektorých anotáciách, ktoré sú v Groovy veľmi užitočné na zníženie štandardného kódu. Mnohé z nich sú k dispozícii na webe groovy.transformácia balíček.

Ak budeme starostlivo analyzovať, uvedomíme si, že niekoľko anotácií poskytuje funkcie podobné Java v projekte Lombok.

4.1. @Natiahnuť

The @Natiahnuť anotácia pridáva predvolenú implementáciu natiahnuť metóda do triedy v čase kompilácie. Všetko, čo potrebujeme, je pridať do triedy anotáciu.

Napríklad pridajme @Natiahnuť anotácia k našej Zamestnanec trieda:

@ToString trieda Zamestnanec {long id String firstName String lastName int age}

Teraz vytvoríme objekt Zamestnanec triedy a overte reťazec vrátený natiahnuť metóda:

Zamestnanec zamestnanec = nový Employee () employee.id = 1 employee.firstName = "norman" employee.lastName = "lewis" employee.age = 28 assert employee.toString () == "com.baeldung.metaprogramming.Employee (1, norman, lewis, 28) "

Môžeme deklarovať aj parametre ako napr vylučuje, zahŕňa, zahrnúťBalík a ignoreNulls s @Natiahnuť upraviť výstupný reťazec.

Napríklad vylúčme id a balíček z reťazca objektu Employee:

@ToString (includePackage = false, vylučuje = ['id'])
assert employee.toString () == "Zamestnanec (norman, lewis, 28)"

4.2. @TupleConstructor

Použite @TupleConstructor v Groovy pridať parametrizovaný konštruktor do triedy. Táto anotácia vytvorí konštruktor s parametrom pre každú vlastnosť.

Napríklad pridajme @TupleConstructor do Zamestnanec trieda:

@TupleConstructor class Employee {long id String firstName String lastName int age}

Teraz môžeme vytvárať Zamestnanec parametre odovzdávania objektov v poradí podľa vlastností definovaných v triede.

Zamestnanec norman = nový Zamestnanec (1, "norman", "lewis", 28) tvrdí norman.toString () == "Zamestnanec (norman, lewis, 28)" 

Ak pri vytváraní objektov neposkytneme vlastnostiam hodnoty, Groovy zváži predvolené hodnoty:

Employee snape = new Employee (2, "snape") assert snape.toString () == "Employee (snape, null, 0)"

Podobný @Natiahnuť, môžeme deklarovať parametre ako napr vylučuje, zahŕňa a includeSuperProperties s @TupleConstructor podľa potreby zmeniť správanie pridruženého konštruktora.

4.3. @EqualsAndHashCode

Môžeme použiť @EqualsAndHashCode vygenerovať predvolenú implementáciu rovná sa a hashCode metódy v čase kompilácie.

Poďme si overiť správanie @EqualsAndHashCode pridaním do súboru Zamestnanec trieda:

Zamestnanec normanCopy = nový Zamestnanec (1, "norman", "lewis", 28) presadzovať norman == normanCopy presadzovať norman.hashCode () == normanCopy.hashCode ()

4.4. @ Kanonický

@ Kanonický je kombináciou @Natiahnuť, @TupleConstructora @EqualsAndHashCode anotácie.

Iba jeho pridaním môžeme ľahko zahrnúť všetky tri do triedy Groovy. Tiež môžeme vyhlásiť @ Kanonický s ktorýmkoľvek zo špecifických parametrov všetkých troch anotácií.

4.5. @AutoClone

Rýchly a spoľahlivý spôsob implementácie Cloneable rozhranie je pridaním @AutoClone anotácia.

Poďme si to overiť klon metóda po pridaní @AutoClone do Zamestnanec trieda:

skúsiť {Employee norman = new Employee (1, "norman", "lewis", 28) def normanCopy = norman.clone () assert norman == normanCopy} catch (CloneNotSupportedException e) {e.printStackTrace ()}

4.6. Podpora protokolovania s @Log, @Commons, @ Log4j, @ Log4j2, a @ Slf4j

Ak chcete pridať podporu protokolovania do akejkoľvek triedy Groovy, stačí, keď pridáte anotácie dostupné v groovy.util.logovanie balíček.

Poďme povoliť protokolovanie poskytované JDK pridaním @Log anotácia k Zamestnanec trieda. Potom pridáme logEmp metóda:

def logEmp () {log.info "Zamestnanec: $ priezvisko, $ meno je vo veku $ rokov rokov veku"}

Volá sa logEmp metóda na Zamestnanec objekt zobrazí protokoly na konzole:

Zamestnanec zamestnanec = nový Zamestnanec (1, „Norman“, „Lewis“, 28) employee.logEmp ()
INFO: Zamestnanec: Lewis, Norman má 28 rokov

Podobne @ Commons anotácia je k dispozícii na pridanie podpory protokolovania Apache Commons. @ Log4j je k dispozícii pre podporu protokolovania Apache Log4j 1.x a @ Log4j2 pre Apache Log4j 2.x. Nakoniec použite @ Slf4j pridať podporu jednoduchého protokolovania pre podporu Java.

5. Záver

V tomto tutoriáli sme preskúmali koncept metaprogramovania v Groovy.

Pozdĺž cesty sme videli niekoľko pozoruhodných funkcií metaprogramovania pre beh aj pre kompiláciu.

Zároveň sme preskúmali ďalšie užitočné anotácie dostupné v aplikácii Groovy pre čistší a dynamickejší kód.

Implementácie kódu pre tento článok sú ako obvykle k dispozícii na serveri GitHub.


$config[zx-auto] not found$config[zx-overlay] not found