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: 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: Rovnaká metóda môže mať aj druhý argument ako hodnotu vlastnosti, aby zachytila volanie metódy setter chýbajúcej vlastnosti: 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: Takže namiesto zabalenia volania metódy do a Skús chytiť, môžeme definovať methodMissing: 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ť: Ak sa posunieme ďalej, doplníme chýbajúce getFullName metóda do Zamestnanec objekt triedy za behu: Podobne môžeme pridať konštruktor do Zamestnanec trieda za behu: 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: 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: 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: Poďme si to overiť getYearOfBirth metóda pridaná do Zamestnanec trieda: 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: Potom povolíme StaticEmployeeExtension pridaním nasledujúcej konfigurácie do ExtensionModule spis: Všetko, čo potrebujeme, je vyskúšať statickýgetDefaultObj metóda na Zamestnanec trieda: Podobne pomocou rozšírení môžeme pridať metódu do predkompilovaných tried Java Páči sa mi to Celé číslo a Dlhé: 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. 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: Teraz vytvoríme objekt Zamestnanec triedy a overte reťazec vrátený natiahnuť metóda: 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: 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: Teraz môžeme vytvárať Zamestnanec parametre odovzdávania objektov v poradí podľa vlastností definovaných v triede. Ak pri vytváraní objektov neposkytneme vlastnostiam hodnoty, Groovy zváži predvolené hodnoty: 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. 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: @ 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í. 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: 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: Volá sa logEmp metóda na Zamestnanec objekt zobrazí protokoly na konzole: 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. 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.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
def propertyMissing (reťazec propertyName) {"vlastnosť '$ propertyName' nie je k dispozícii"}
assert emp.address == "adresa 'nehnuteľnosti' nie je k dispozícii"
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ú
skúste {emp.getFullName ()} chytiť (MissingMethodException e) {println "metóda nie je definovaná"}
def methodMissing (String methodName, def methodArgs) {"metóda '$ methodName' nie je definovaná"}
assert emp.getFullName () == "metóda 'getFullName' nie je definovaná"
3.3. ExpandoMetaClass
Employee.metaClass.address = ""
Zamestnanec emp = nový Zamestnanec (meno: "Norman", priezvisko: "Lewis", adresa: "US") assert emp.address == "US"
emp.metaClass.getFullName = {"$ priezvisko, $ krstne meno"}
assert emp.getFullName () == "Lewis, Norman"
Employee.metaClass.constructor = {Reťazec meno -> nový zamestnanec (meno: meno)}
Zamestnanec norman = nový Zamestnanec („Norman“) presadzuje norman.firstName == „Norman“ presadzuje norman.lastName == null
String.metaClass.capitalize = {String str -> str.substring (0, 1) .toUpperCase () + str.substring (1)}
tvrdiť „norman“ .capitalize () == „norman“
3.4. Prípony
trieda BasicExtensions {static int getYearOfBirth (zamestnanec samostatne) {návrat Year.now (). hodnota - self.age}}
moduleName = core-groovy-2 moduleVersion = 1.0-SNAPSHOT extensionClasses = com.baeldung.metaprogramming.extension.BasicExtensions
def vek = 28 def expectYearOfBirth = Year.now () - vek Zamestnanec emp = nový Zamestnanec (vek: vek) uplatniť emp.getYearOfBirth () == expectYearOfBirth.value
trieda StaticEmployeeExtension {statický zamestnanec getDefaultObj (samostatne zamestnanec) {vrátiť nového zamestnanca (meno: "meno", priezvisko: "priezvisko", vek: 20)}}
staticExtensionClasses = com.baeldung.metaprogramming.extension.StaticEmployeeExtension
assert Employee.getDefaultObj (). firstName == "firstName" assert Employee.getDefaultObj (). lastName == "lastName" assert Employee.getDefaultObj (). age == 20
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
4.1. @Natiahnuť
@ToString trieda Zamestnanec {long id String firstName String lastName int age}
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) "
@ToString (includePackage = false, vylučuje = ['id'])
assert employee.toString () == "Zamestnanec (norman, lewis, 28)"
4.2. @TupleConstructor
@TupleConstructor class Employee {long id String firstName String lastName int age}
Zamestnanec norman = nový Zamestnanec (1, "norman", "lewis", 28) tvrdí norman.toString () == "Zamestnanec (norman, lewis, 28)"
Employee snape = new Employee (2, "snape") assert snape.toString () == "Employee (snape, null, 0)"
4.3. @EqualsAndHashCode
Zamestnanec normanCopy = nový Zamestnanec (1, "norman", "lewis", 28) presadzovať norman == normanCopy presadzovať norman.hashCode () == normanCopy.hashCode ()
4.4. @ Kanonický
4.5. @AutoClone
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
def logEmp () {log.info "Zamestnanec: $ priezvisko, $ meno je vo veku $ rokov rokov veku"}
Zamestnanec zamestnanec = nový Zamestnanec (1, „Norman“, „Lewis“, 28) employee.logEmp ()
INFO: Zamestnanec: Lewis, Norman má 28 rokov
5. Záver