Metaprogramozás Groovy-ban

1. Áttekintés

A Groovy egy dinamikus és erőteljes JVM nyelv, amely számos funkcióval rendelkezik, például bezárásokkal és tulajdonságokkal.

Ebben az oktatóanyagban a Metaprogramozás fogalmát tárjuk fel Groovy-ban.

2. Mi a metaprogramozás?

A metaprogramozás egy programozási technika, amellyel programot írhatnak saját vagy egy másik program metaadatokkal történő módosítására.

A Groovy-ban metaprogramozást futás közben és fordítási időben egyaránt lehet végrehajtani. A továbbiakban mindkét technika néhány figyelemre méltó tulajdonságát megvizsgáljuk.

3. Futásidejű metaprogramozás

A futásidejű metaprogramozás lehetővé teszi számunkra az osztály meglévő tulajdonságainak és módszereinek megváltoztatását. Ezenkívül új tulajdonságokat és módszereket is csatolhatunk; mind futás közben.

A Groovy néhány módszert és tulajdonságot nyújt, amelyek segítenek megváltoztatni az osztály viselkedését futás közben.

3.1. propertyMissing

Amikor megpróbálunk elérni egy Groovy osztály egy nem definiált tulajdonságát, akkor a MissingPropertyException. A kivétel elkerülése érdekében Groovy biztosítja a propertyMissing módszer.

Először írjunk egy Munkavállaló osztály néhány tulajdonsággal:

osztály Alkalmazott {String keresztnév String vezetékNév int age}

Másodszor létrehozunk egy Munkavállaló objektumot, és próbáljon meg egy meghatározatlan tulajdonságot megjeleníteni cím. Következésképpen meg fogja dobni a MissingPropertyException:

Employee emp = new Employee (keresztnév: "Norman", vezetéknév: "Lewis") println emp.cím 
groovy.lang.MissingPropertyException: Nincs ilyen tulajdonság: osztály címe: com.baeldung.metaprogramming.Employee

Groovy biztosítja a propertyMissing módszer a hiányzó ingatlankérés befogására. Ezért elkerülhetjük a MissingPropertyException futás közben.

A hiányzó tulajdonság getter metódushívásának megfogásához egyetlen argumentummal definiáljuk a tulajdonság nevét:

def propertyMissing (karakterlánc tulajdonságnév) {"tulajdonság '$ propertyName' nem érhető el"}
assert emp.address == "tulajdonság" cím "nem érhető el"

Ugyanennek a metódusnak lehet a második argumentuma is, mint a tulajdonság értéke, hogy elkapja a hiányzó tulajdonság setter metódushívását:

def propertyMissing (karakterlánc tulajdonságnév, tulajdonságValue) {println "nem állíthatja be a $ propertyValue értéket - a '$ propertyName' tulajdonság nem érhető el"}

3.2. methodMissing

A methodMissing módszer hasonló propertyMissing. Azonban, methodMissing lehallgat minden hiányzó módszer hívását, elkerülve ezzel a MissingMethodException.

Próbáljuk meg felhívni a getFullName módszer egy Munkavállaló tárgy. Mint getFullName hiányzik, a végrehajtás eldobja a MissingMethodException futás közben:

próbáld meg az {emp.getFullName ()} catch (MissingMethodException e) {println "metódus nincs meghatározva"}

Tehát ahelyett, hogy egy metódust hívna be a próbáld elkapni, meghatározhatjuk methodMissing:

def methodMissing (karakterlánc metódusnév, def módszerArgs) {"metódus '$ methodName' nincs meghatározva"}
assert emp.getFullName () == "A 'getFullName' metódus nincs meghatározva"

3.3. ExpandoMetaClass

Groovy biztosítja a metaClass ingatlan minden osztályában. A metaClass tulajdonság a ExpandoMetaClass.

A ExpandoMetaClass osztály számos módot kínál a meglévő osztály futás közbeni átalakítására. Hozzáadhatunk például tulajdonságokat, módszereket vagy konstruktorokat.

Először tegyük hozzá a hiányzókat cím tulajdon a Munkavállaló osztály felhasználásával metaClass ingatlan:

Employee.metaClass.address = ""
Employee emp = new Employee (keresztnév: "Norman", vezetéknév: "Lewis", cím: "US") állítja emp.cím == "US"

Tovább haladva tegyük hozzá a hiányzókat getFullName módszer a Munkavállaló osztály objektum futás közben:

emp.metaClass.getFullName = {"$ vezetékNév, $ firstName"}
assert emp.getFullName () == "Lewis, Norman"

Hasonlóképpen hozzáadhatunk egy konstruktort a Munkavállaló osztály futás közben:

Employee.metaClass.constructor = {Karakterlánc keresztnév -> új alkalmazott (keresztnév: keresztnév)}
Munkavállaló norman = új Alkalmazott ("Norman") állítja norman.firstName == "Norman" állítja norman.lastName == null

Hasonlóképpen hozzátehetjük statikus módszerek felhasználásával metaClass.static.

A metaClass tulajdonság nem csak a felhasználó által definiált osztályok módosítására szolgál, hanem a meglévő Java osztályokra is futás közben.

Például tegyünk hozzá egy a-t nagybetűs módszer a Húr osztály:

String.metaClass.capitalize = {String str -> str.substring (0, 1) .toUpperCase () + str.substring (1)}
állítsd a "norman" -t. kapitalize () == "Norman"

3.4. Hosszabbítások

A kiterjesztés futás közben hozzáadhat egy módszert egy osztályhoz, és globálisan elérhetővé teheti azt.

A kiterjesztésben definiált módszereknek mindig statikusaknak kell lenniük, a maga osztály objektum első argumentumként.

Írjunk például egy BasicExtension osztály hozzáadni a getYearOfBirth módszer a Munkavállaló osztály:

class BasicExtensions {static int getYearOfBirth (Employee self) {return Year.now (). value - self.age}}

A BasicExtensions, hozzá kell adnunk a konfigurációs fájlt a META-INF / szolgáltatások projektünk könyvtárát.

Tehát tegyük hozzá a org.codehaus.groovy.runtime.ExtensionModule fájl a következő konfigurációval:

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

Ellenőrizzük a getYearOfBirth módszer a Munkavállaló osztály:

def age = 28 def várhatóYearOfBirth = Year.now () - age Employee emp = new Employee (age: age) állítsa emp.getYearOfBirth () == várhatóYearOfBirth.value

Hasonlóképpen hozzá statikus módszereket, meg kell határoznunk egy külön kiterjesztési osztályt.

Például tegyünk hozzá egy a-t statikus módszer getDefaultObj a miénknek Munkavállaló osztály meghatározásával StaticEmployeeExtension osztály:

class StaticEmployeeExtension {static Employee getDefaultObj (Employee self) {return new Employee (firstName: "firstName", lastName: "vezetékNév", age: 20)}}

Ezután engedélyezzük a StaticEmployeeExtension a következő konfiguráció hozzáadásával a ExtensionModule fájl:

staticExtensionClasses = com.baeldung.metaprogramming.extension.StaticEmployeeExtension

Most csak arra van szükségünk, hogy teszteljük statikusgetDefaultObj módszer a Munkavállaló osztály:

állítsa Employee.getDefaultObj (). firstName == "keresztnév" assert Employee.getDefaultObj (). lastName == "lastName" állítsa Employee.getDefaultObj (). age == 20

Hasonlóképpen, kiterjesztésekkel hozzáadhatunk egy módszert az előre lefordított Java osztályokhoz mint Egész szám és Hosszú:

public static void printCounter (Egész én) {while (self> 0) {println self self}} return self} állítás 5.printCounter () == 0 
public static Hosszú négyzet (Long self) {return self * self} 40l.square () == 1600l 

4. Fordítási idejű metaprogramozás

Konkrét annotációk segítségével könnyedén megváltoztathatjuk az osztály struktúráját fordítás idején. Más szavakkal, annotációkkal módosíthatjuk az osztály absztrakt szintaxisfáját az összeállításnál.

Beszéljünk néhány olyan kommentárról, amelyek nagyon hasznosak a Groovy-ban a kazánlap kódjának csökkentése érdekében. Közülük sok elérhető a groovy.transzformáció csomag.

Ha alaposan elemezzük, rájövünk, hogy néhány kommentár a Java Project Lombokhoz hasonló funkciókat kínál.

4.1. @ToString

A @ToString kommentár hozzáadja a Sztring módszer egy osztályhoz fordítási időben. Csak arra van szükségünk, hogy hozzáadjuk az annotációt az osztályhoz.

Például tegyük hozzá a @ToString annotáció a mi Munkavállaló osztály:

@ToString osztály Alkalmazott {long id String firstName String vezetéknév int age}

Most létrehozunk egy objektumot a Munkavállaló osztály és ellenőrizze a Sztring módszer:

Alkalmazott alkalmazott = új Alkalmazott () alkalmazott.id = 1 alkalmazott.névNév = "norman" alkalmazott.névNév = "lewis" alkalmazott.kor = 28 állítja alkalmazott.toString () == "com.baeldung.metaprogramming.Employee (1, norman, lewis, 28) "

Deklarálhatunk olyan paramétereket is, mint kizárja, magába foglalja, includePackage és ignoreNulls val vel @ToString hogy módosítsa a kimeneti karakterláncot.

Például zárjuk ki id és csomag az Employee objektum karakterláncából:

@ToString (includePackage = hamis, kizárja = ['id'])
állítsd alkalmazott.toString () == "Alkalmazott (norman, lewis, 28)"

4.2. @TupleConstructor

Használat @TupleConstructor a Groovy-ban egy paraméterezett konstruktor hozzáadásához az osztályba. Ez a feljegyzés létrehoz egy konstruktort, amelynek paraméterei vannak az egyes tulajdonságokhoz.

Például tegyük hozzá @TupleConstructor hoz Munkavállaló osztály:

@TupleConstructor osztály Alkalmazott {long id String firstName String vezetékNév int age}

Most létrehozhatunk Munkavállaló az objektum átadásának paraméterei az osztályban meghatározott tulajdonságok sorrendjében.

Alkalmazott norman = új alkalmazott (1, "norman", "lewis", 28) állítja a norman.toString () == "Alkalmazott (norman, lewis, 28)" 

Ha objektumok létrehozása közben nem adunk meg értékeket a tulajdonságoknak, a Groovy az alapértelmezett értékeket veszi figyelembe:

Alkalmazott snape = new Employee (2, "snape") állítja a snape.toString () == "Alkalmazott (snape, null, 0)"

Hasonló @ToString, deklarálhatunk olyan paramétereket, mint kizárja, magába foglalja és includeSuperProperties val vel @TupleConstructor szükség szerint módosítsa a hozzá kapcsolódó konstruktor viselkedését.

4.3. @EqualsAndHashCode

Tudjuk használni @EqualsAndHashCode generálja az alapértelmezett megvalósítását egyenlő és hash kód módszerek a fordítás idején.

Ellenőrizzük a @EqualsAndHashCode hozzáadásával a Munkavállaló osztály:

Alkalmazott normanCopy = new Employee (1, "norman", "lewis", 28) állítja, hogy norman == normanCopy állítja, hogy norman.hashCode () == normanCopy.hashCode ()

4.4. @Kánoni

@Kánoni kombinációja @ToString, @TupleConstructor, és @EqualsAndHashCode annotációk.

Csak hozzáadásával mindhármat könnyedén felvehetjük egy Groovy osztályba. Azt is kijelenthetjük @Kánoni mindhárom feljegyzés bármelyikének speciális paraméterével.

4.5. @AutoClone

Gyors és megbízható megvalósítási mód Klónozható interfész a @AutoClone annotáció.

Ellenőrizzük a klón módszer hozzáadás után @AutoClone hoz Munkavállaló osztály:

próbáld ki {Employee norman = new Employee (1, "norman", "lewis", 28) def normanCopy = norman.clone () assert norman == normanCopy} catch (CloneNotSupportedException e) {e.printStackTrace ()}

4.6. Naplózási támogatás @Log, @Commons, @ Log4j, @ Log4j2, és @ Slf4j

A naplózási támogatás hozzáadásához bármely Groovy osztályhoz csak annyit kell tennünk, hogy hozzáadjuk a groovy.util.logging csomag.

Engedélyezzük a JDK által biztosított naplózást a @Log jegyzet a Munkavállaló osztály. Utána hozzáadjuk a logEmp módszer:

def logEmp () {log.info "Alkalmazott: $ lastName, $ firstName $ age éves korú"}

Felhívás a logEmp módszer egy Munkavállaló Az objektum megmutatja a naplókat a konzolon:

Alkalmazott alkalmazott = új alkalmazott (1, "Norman", "Lewis", 28) alkalmazott.logEmp ()
INFO: Alkalmazott: Lewis, Norman 28 éves

Hasonlóképpen a @Commons az Apache Commons naplózási támogatásának hozzáadásához annotáció áll rendelkezésre. @ Log4j elérhető az Apache Log4j 1.x naplózás támogatásához és @ Log4j2 az Apache Log4j 2.x-hez. Végül használja @ Slf4j az egyszerű naplózási homlokzat hozzáadása a Java támogatáshoz.

5. Következtetés

Ebben az oktatóanyagban a Groovy-ban feltártuk a metaprogramozás fogalmát.

Útközben láttunk néhány figyelemre méltó metaprogramozási funkciót mind a futás, mind a fordítás idejére vonatkozóan.

Ugyanakkor további, a Groovy-ban elérhető praktikus jegyzeteket is feltártunk a tisztább és dinamikusabb kódokhoz.

Szokás szerint a cikk kód implementációi elérhetők a GitHubon.