Metaprogramiranje u Groovyju

1. Pregled

Groovy je dinamičan i moćan JVM jezik koji ima brojne značajke poput zatvaranja i osobina.

U ovom uputstvu istražit ćemo koncept metaprogramiranja u Groovyju.

2. Što je metaprogramiranje?

Metaprogramiranje je tehnika programiranja pisanja programa za modificiranje sebe ili drugog programa pomoću metapodataka.

U Groovyju je moguće izvoditi metaprogramiranje i za vrijeme izvođenja i za vrijeme kompajliranja. Ubuduće ćemo istražiti nekoliko značajnih značajki obje tehnike.

3. Runtime metaprogramiranje

Runtime metaprogramiranje omogućuje nam izmjenu postojećih svojstava i metoda klase. Također, možemo priložiti nova svojstva i metode; sve u vrijeme izvođenja.

Groovy nudi nekoliko metoda i svojstava koja pomažu u promjeni ponašanja klase u vrijeme izvođenja.

3.1. svojstvoMassing

Kada pokušamo pristupiti nedefiniranom svojstvu klase Groovy, ono baca a MissingPropertyException. Da bi se izbjegla iznimka, Groovy nudi svojstvoMassing metoda.

Prvo, napišimo Zaposlenik razred s nekim svojstvima:

class Zaposlenik {String firstName String lastName int age}

Drugo, stvorit ćemo Zaposlenik objekt i pokušajte prikazati nedefinirano svojstvo adresa. Slijedom toga, bacit će MissingPropertyException:

Zaposlenik emp = novi Zaposlenik (ime: "Norman", prezime: "Lewis") println emp.address 
groovy.lang.MissingPropertyException: Nema takvog svojstva: adresa za klasu: com.baeldung.metaprogramming.E Employee

Groovy nudi svojstvoMassing metoda za hvatanje zahtjeva za svojstvom koji nedostaje. Stoga možemo izbjeći a MissingPropertyException za vrijeme izvođenja.

Da bismo uhvatili poziv getter metode nedostajućeg svojstva, definirat ćemo ga jednim argumentom za ime svojstva:

def propertyMissing (String propertyName) {"svojstvo '$ propertyName' nije dostupno"}
potvrditi emp.address == "adresa 'svojstva' nije dostupna"

Također, ista metoda može imati drugi argument kao vrijednost svojstva, da uhvati poziv metode postavljača nedostajućeg svojstva:

def propertyMissing (Niz svojstvaName, svojstvoValue) {println "ne može postaviti $ propertyValue - svojstvo '$ propertyName' nije dostupno"}

3.2. metodaMassing

The metodaMassing metoda je slična svojstvoMassing. Međutim, metodaMassing presreće poziv za bilo koji nedostajući način, izbjegavajući tako MissingMethodException.

Pokušajmo nazvati getFullName metoda na an Zaposlenik objekt. Kao getFullName nedostaje, izvršenje će baciti MissingMethodException za vrijeme izvođenja:

isprobajte {emp.getFullName ()} catch (MissingMethodException e) {println "metoda nije definirana"}

Dakle, umjesto umotavanja poziva metode u pokušaj uhvatiti, možemo definirati metodaMassing:

def methodMissing (String methodName, def methodArgs) {"metoda '$ methodName' nije definirana"}
potvrditi emp.getFullName () == "metoda 'getFullName' nije definirana"

3.3. ExpandoMetaClass

Groovy pruža a metaKlasa svojstvo u svim svojim klasama. The metaKlasa svojstvo se odnosi na instancu ExpandoMetaClass.

The ExpandoMetaClass klasa pruža brojne načine za transformiranje postojeće klase u vrijeme izvođenja. Na primjer, možemo dodati svojstva, metode ili konstruktore.

Prvo, dodajmo nedostajuće adresa vlasništvo na Zaposlenik razred koristeći metaKlasa svojstvo:

Employee.metaClass.address = ""
Zaposlenik emp = novi Zaposlenik (ime: "Norman", prezime: "Lewis", adresa: "US") potvrditi emp.address == "US"

Krećući se dalje, dodajmo nedostajuće getFullName metoda za Zaposlenik objekt klase u vrijeme izvođenja:

emp.metaClass.getFullName = {"$ lastName, $ firstName"}
potvrditi emp.getFullName () == "Lewis, Norman"

Slično tome, možemo dodati konstruktor u Zaposlenik klasa u vrijeme izvođenja:

Employee.metaClass.constructor = {String firstName -> novi Employee (firstName: firstName)}
Zaposlenik norman = novi zaposlenik ("Norman") potvrđuje norman.firstName == "Norman" potvrđuje norman.lastName == null

Isto tako, možemo dodati statički metode pomoću metaClass.static.

The metaKlasa svojstvo nije samo zgodno za modificiranje korisnički definiranih klasa, već i postojećih Java klasa u vrijeme izvođenja.

Na primjer, dodajmo a kapitalizirati metoda za Niz razred:

String.metaClass.capitalize = {Niz str -> str.substring (0, 1) .toUpperCase () + str.substring (1)}
ustvrditi "norman" .capitalize () == "Norman"

3.4. Proširenja

Proširenje može dodati metodu klasi tijekom izvođenja i učiniti je globalno dostupnom.

Metode definirane u proširenju trebaju uvijek biti statične, s oznakom sebe objekt klase kao prvi argument.

Na primjer, napišite a BasicExtention razred dodati a getYearOfBirth metoda za Zaposlenik razred:

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

Da biste omogućili BasicExtentions, morat ćemo dodati konfiguracijsku datoteku u META-INF / usluge direktorij našeg projekta.

Dakle, dodajmo org.codehaus.groovy.runtime.ExtensumModule datoteka sa sljedećom konfiguracijom:

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

Provjerimo getYearOfBirth metoda dodana u Zaposlenik razred:

def age = 28 def očekivanoYearOfBirth = Year.now () - dob zaposlenik emp = novi zaposlenik (dob: dob) potvrditi emp.getYearOfBirth () == očekuje se godinaBrth.value

Slično tome, dodati statički metode u klasi, morat ćemo definirati zasebnu nastavnu klasu.

Na primjer, dodajmo a statički metoda getDefaultObj našem Zaposlenik razred definiranjem Proširenje StaticEfficieee razred:

klasa StaticEfficieeExtension {static Employee getDefaultObj (Self Employee) {return new Employee (firstName: "firstName", lastName: "lastName", dob: 20)}}

Zatim omogućujemo Proširenje StaticEfficieee dodavanjem sljedeće konfiguracije u ExtensionModule datoteka:

staticExtensionClasses = com.baeldung.metaprogramming.extension.StaticEfficieeExtension

Sada je sve što trebamo testirati statičkigetDefaultObj metoda na Zaposlenik razred:

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

Slično tome, pomoću ekstenzija, možemo dodati metodu u prethodno sastavljene Java klase Kao Cijeli broj i Dugo:

javna statička praznina printCounter (Integer self) {while (self> 0) {println self self--} return self} potvrditi 5.printCounter () == 0 
javni statički Long square (Long self) {return self * self} tvrditi 40l.square () == 1600l 

4. Programirano metaprogramiranje

Koristeći određene napomene, možemo bez napora izmijeniti strukturu klase u vrijeme sastavljanja. Drugim riječima, možemo upotrijebiti napomene za modificiranje stabla apstraktne sintakse klase na kompilaciji.

Razgovarajmo o nekim napomenama koje su u Groovyu vrlo korisne za smanjenje koda. Mnogi od njih dostupni su u groovy.transform paket.

Ako pažljivo analiziramo, shvatit ćemo da nekoliko napomena pruža značajke slične Javinom projektu Lombok.

4.1. @ToString

The @ToString napomena dodaje zadanu implementaciju toString metodu u razred u vrijeme prevođenja. Sve što trebamo je dodati napomenu u razred.

Na primjer, dodajmo @ToString napomena za našu Zaposlenik razred:

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

Sada ćemo stvoriti objekt Zaposlenik klase i provjerite niz koji je vratio toString metoda:

Zaposlenik zaposlenik = novi Zaposlenik () zaposlenik.id = 1 zaposlenik.Prvoime = "norman" zaposlenik.lastName = "lewis" zaposlenik.stranica = 28 tvrditi zaposlenik.toString () == "com.baeldung.metaprogramming.Eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeu раt.prets) norman, lewis, 28) "

Također možemo deklarirati parametre kao što su isključuje, uključuje, uključujuPaket i ignoreNulls s @ToString za izmjenu izlaznog niza.

Na primjer, izuzmimo iskaznica i paket iz niza objekta Employee:

@ToString (includePackage = false, excludes = ['id'])
tvrditi zaposlenik.toString () == "Zaposlenik (norman, lewis, 28)"

4.2. @TupleConstructor

Koristiti @TupleConstructor u Groovy za dodavanje parametriziranog konstruktora u klasu. Ova napomena stvara konstruktor s parametrom za svako svojstvo.

Na primjer, dodajmo @TupleConstructor prema Zaposlenik razred:

Klasa @TupleConstructor Zaposlenik {long id String firstName String lastName int age}

Sada možemo stvarati Zaposlenik objekt koji prosljeđuje parametre redoslijedom svojstava definiranih u klasi.

Zaposlenik norman = novi Zaposlenik (1, "norman", "lewis", 28) tvrdi norman.toString () == "Zaposlenik (norman, lewis, 28)" 

Ako svojstvima ne pružimo vrijednosti tijekom stvaranja objekata, Groovy će razmotriti zadane vrijednosti:

Snape zaposlenika = novi Employee (2, "snape") potvrdi snape.toString () == "Employee (snape, null, 0)"

Slično @ToString, možemo deklarirati parametre kao što su isključuje, uključuje i includeSuperProperties s @TupleConstructor kako bi promijenio ponašanje povezanog konstruktora po potrebi.

4.3. @EqualsAndHashCode

Možemo koristiti @EqualsAndHashCode za generiranje zadane implementacije jednako i hashCode metode u vrijeme kompajliranja.

Provjerimo ponašanje @EqualsAndHashCode dodavanjem u Zaposlenik razred:

Zaposlenik normanCopy = novi Zaposlenik (1, "norman", "lewis", 28) potvrdi norman == normanCopy potvrdi norman.hashCode () == normanCopy.hashCode ()

4.4. @Kanonski

@Kanonski je kombinacija @ToString, @TupleConstructor, i @EqualsAndHashCode bilješke.

Samo dodavanjem, lako možemo sve tri uključiti u Groovy klasu. Također, možemo se izjasniti @Kanonski s bilo kojim od specifičnih parametara sve tri bilješke.

4.5. @AutoClone

Brz i pouzdan način implementacije Klonirajući sučelje je dodavanjem @AutoClone bilješka.

Provjerimo klon metoda nakon dodavanja @AutoClone prema Zaposlenik razred:

pokušajte {Zaposlenik norman = novi zaposlenik (1, "norman", "lewis", 28) def normanCopy = norman.clone () potvrditi norman == normanCopy} ulov (CloneNotSupportedException e) {e.printStackTrace ()}

4.6. Podrška za prijavu sa @Prijava, @Commons, @ Log4j, @ Log4j2, i @ Slf4j

Da bismo dodali podršku za bilježenje bilo kojoj klasi Groovy, sve što trebamo je dodati bilješke dostupne u groovy.util.logging paket.

Omogućimo evidentiranje koje pruža JDK dodavanjem @Prijava bilješka na Zaposlenik razred. Poslije ćemo dodati logEmp metoda:

def logEmp () {log.info "Zaposlenik: $ lastName, $ firstName je $ age godine starosti"}

Pozivanje logEmp metoda na an Zaposlenik objekt prikazat će zapisnike na konzoli:

Zaposlenik zaposlenik = novi zaposlenik (1, "Norman", "Lewis", 28) zaposlenik.logEmp ()
INFO: Zaposlenik: Lewis, Norman ima 28 godina

Slično tome, @Commons napomena je dostupna za dodavanje podrške za prijavljivanje Apache Commons. @ Log4j dostupan je za podršku prijavljivanja Apache Log4j 1.x i @ Log4j2 za Apache Log4j 2.x. Napokon, upotrijebite @ Slf4j za dodavanje Simple Logging Facade za Java podršku.

5. Zaključak

U ovom uputstvu istražili smo koncept metaprogramiranja u Groovyju.

Usput smo vidjeli nekoliko značajnih značajki metaprogramiranja kako za vrijeme izvođenja tako i za vrijeme kompajliranja.

Istodobno smo istražili dodatne korisne napomene dostupne u Groovyju za čišći i dinamički kôd.

Kao i obično, implementacije koda za ovaj članak dostupne su na GitHubu.


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