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: 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: Također, ista metoda može imati drugi argument kao vrijednost svojstva, da uhvati poziv metode postavljača nedostajućeg svojstva: 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: Dakle, umjesto umotavanja poziva metode u pokušaj uhvatiti, možemo definirati metodaMassing: 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: Krećući se dalje, dodajmo nedostajuće getFullName metoda za Zaposlenik objekt klase u vrijeme izvođenja: Slično tome, možemo dodati konstruktor u Zaposlenik klasa u vrijeme izvođenja: 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: 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: 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: Provjerimo getYearOfBirth metoda dodana u Zaposlenik razred: 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: Zatim omogućujemo Proširenje StaticEfficieee dodavanjem sljedeće konfiguracije u ExtensionModule datoteka: Sada je sve što trebamo testirati statičkigetDefaultObj metoda na Zaposlenik razred: Slično tome, pomoću ekstenzija, možemo dodati metodu u prethodno sastavljene Java klase Kao Cijeli broj i Dugo: 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. 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: Sada ćemo stvoriti objekt Zaposlenik klase i provjerite niz koji je vratio toString metoda: 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: 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: Sada možemo stvarati Zaposlenik objekt koji prosljeđuje parametre redoslijedom svojstava definiranih u klasi. Ako svojstvima ne pružimo vrijednosti tijekom stvaranja objekata, Groovy će razmotriti zadane vrijednosti: 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. Možemo koristiti @EqualsAndHashCode za generiranje zadane implementacije jednako i hashCode metode u vrijeme kompajliranja. Provjerimo ponašanje @EqualsAndHashCode dodavanjem u Zaposlenik razred: @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. Brz i pouzdan način implementacije Klonirajući sučelje je dodavanjem @AutoClone bilješka. Provjerimo klon metoda nakon dodavanja @AutoClone prema Zaposlenik razred: 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: Pozivanje logEmp metoda na an Zaposlenik objekt prikazat će zapisnike na konzoli: 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. 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.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
def propertyMissing (String propertyName) {"svojstvo '$ propertyName' nije dostupno"}
potvrditi emp.address == "adresa 'svojstva' nije dostupna"
def propertyMissing (Niz svojstvaName, svojstvoValue) {println "ne može postaviti $ propertyValue - svojstvo '$ propertyName' nije dostupno"}
3.2. metodaMassing
isprobajte {emp.getFullName ()} catch (MissingMethodException e) {println "metoda nije definirana"}
def methodMissing (String methodName, def methodArgs) {"metoda '$ methodName' nije definirana"}
potvrditi emp.getFullName () == "metoda 'getFullName' nije definirana"
3.3. ExpandoMetaClass
Employee.metaClass.address = ""
Zaposlenik emp = novi Zaposlenik (ime: "Norman", prezime: "Lewis", adresa: "US") potvrditi emp.address == "US"
emp.metaClass.getFullName = {"$ lastName, $ firstName"}
potvrditi emp.getFullName () == "Lewis, Norman"
Employee.metaClass.constructor = {String firstName -> novi Employee (firstName: firstName)}
Zaposlenik norman = novi zaposlenik ("Norman") potvrđuje norman.firstName == "Norman" potvrđuje norman.lastName == null
String.metaClass.capitalize = {Niz str -> str.substring (0, 1) .toUpperCase () + str.substring (1)}
ustvrditi "norman" .capitalize () == "Norman"
3.4. Proširenja
klasa BasicExtensions {static int getYearOfBirth (zaposlenik self) {return Year.now (). value - self.age}}
moduleName = core-groovy-2 moduleVersion = 1.0-SNAPSHOT extensionClasses = com.baeldung.metaprogramming.extention.BasicExtensions
def age = 28 def očekivanoYearOfBirth = Year.now () - dob zaposlenik emp = novi zaposlenik (dob: dob) potvrditi emp.getYearOfBirth () == očekuje se godinaBrth.value
klasa StaticEfficieeExtension {static Employee getDefaultObj (Self Employee) {return new Employee (firstName: "firstName", lastName: "lastName", dob: 20)}}
staticExtensionClasses = com.baeldung.metaprogramming.extension.StaticEfficieeExtension
potvrditi Employee.getDefaultObj (). firstName == "firstName" potvrditi Employee.getDefaultObj (). lastName == "lastName" potvrditi Employee.getDefaultObj (). age == 20
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
4.1. @ToString
@ToString class Employee {long id String firstName String lastName int age}
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) "
@ToString (includePackage = false, excludes = ['id'])
tvrditi zaposlenik.toString () == "Zaposlenik (norman, lewis, 28)"
4.2. @TupleConstructor
Klasa @TupleConstructor Zaposlenik {long id String firstName String lastName int age}
Zaposlenik norman = novi Zaposlenik (1, "norman", "lewis", 28) tvrdi norman.toString () == "Zaposlenik (norman, lewis, 28)"
Snape zaposlenika = novi Employee (2, "snape") potvrdi snape.toString () == "Employee (snape, null, 0)"
4.3. @EqualsAndHashCode
Zaposlenik normanCopy = novi Zaposlenik (1, "norman", "lewis", 28) potvrdi norman == normanCopy potvrdi norman.hashCode () == normanCopy.hashCode ()
4.4. @Kanonski
4.5. @AutoClone
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
def logEmp () {log.info "Zaposlenik: $ lastName, $ firstName je $ age godine starosti"}
Zaposlenik zaposlenik = novi zaposlenik (1, "Norman", "Lewis", 28) zaposlenik.logEmp ()
INFO: Zaposlenik: Lewis, Norman ima 28 godina
5. Zaključak