Introduksjon til JavaPoet

1. Oversikt

I denne opplæringen vil vi utforske de grunnleggende funksjonene i JavaPoet-biblioteket.

JavaPoet er utviklet av Square, som gir APIer for å generere Java-kildekode. Det kan generere primitive typer, referansetyper og deres varianter (for eksempel klasser, grensesnitt, oppregnede typer, anonyme indre klasser), felt, metoder, parametere, merknader og Javadocs.

JavaPoet administrerer automatisk import av avhengige klasser. Den bruker også Builder-mønsteret til å spesifisere logikken for å generere Java-kode.

2. Maven avhengighet

For å bruke JavaPoet kan vi laste ned den nyeste JAR-filen direkte, eller definere følgende avhengighet i vår pom.xml:

 com.squareup javapoet 1.10.0 

3. Metodespesifikasjon

La oss først gå gjennom metodespesifikasjonen. For å generere en metode kaller vi bare methodBuilder () Metode av MethodSpec klasse. Vi spesifiserer det genererte metodenavnet som en String argument av methodBuilder () metode.

Vi kan generere en enkelt logisk uttalelse som slutter med semikolon bruker addStatement () metode. I mellomtiden kan vi definere en kontrollflyt begrenset med krøllete parenteser, for eksempel hvis-annet blokk, eller til sløyfe, i en kontrollflyt.

Her er et raskt eksempel - generere sumOfTen () metode som beregner summen av tall fra 0 til 10:

MethodSpec sumOfTen = MethodSpec .methodBuilder ("sumOfTen") .addStatement ("int sum = 0") .beginControlFlow ("for (int i = 0; i <= 10; i ++)") .addStatement ("sum + = i" ) .endControlFlow () .build ();

Dette vil gi følgende utdata:

ugyldig sumOfTen () {int sum = 0; for (int i = 0; i <= 10; i ++) {sum + = i; }}

4. Kodeblokk

Vi kan også pakke en eller flere kontrollflyter og logiske utsagn inn i en kodeblokk:

CodeBlock sumOfTenImpl = CodeBlock .builder () .addStatement ("int sum = 0") .beginControlFlow ("for (int i = 0; i <= 10; i ++)") .addStatement ("sum + = i"). EndControlFlow () .bygge();

Som genererer:

int sum = 0; for (int i = 0; i <= 10; i ++) {sum + = i; }

Vi kan forenkle den tidligere logikken i MethodSpec ved å ringe legg til kode () og tilby sumOfTenImpl gjenstand:

MethodSpec sumOfTen = MethodSpec .methodBuilder ("sumOfTen") .addCode (sumOfTenImpl) .build ();

En kodeblokk gjelder også for andre spesifikasjoner, for eksempel typer og Javadocs.

5. Feltspesifikasjon

Neste - la oss utforske feltspesifikasjonslogikken.

For å generere et felt bruker vi bygger() metoden for FieldSpec klasse:

FieldSpec name = FieldSpec .builder (String.class, "name") .addModifiers (Modifier.PRIVATE) .build ();

Dette vil generere følgende felt:

privat strengnavn;

Vi kan også initialisere standardverdien for et felt ved å ringe initializer () metode:

FieldSpec defaultName = FieldSpec .builder (String.class, "DEFAULT_NAME") .addModifiers (Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL) .initializer ("\" Alice \ "") .build ();

Som genererer:

privat statisk finale String DEFAULT_NAME = "Alice";

6. Parameterspesifikasjon

La oss nå utforske parameterspesifikasjonslogikken.

I tilfelle vi vil legge til en parameter i metoden, kan vi ringe addParameter () innenfor kjeden til funksjonen kaller inn byggherren.

I tilfelle mer komplekse parametertyper kan vi benytte oss av ParameterSpec bygger:

ParameterSpec strings = ParameterSpec .builder (ParameterizedTypeName.get (ClassName.get (List.class), TypeName.get (String.class)), "strings") .build ();

Vi kan også legge til modifikatoren for metoden, for eksempel offentlig og / eller statisk:

MethodSpec sumOfTen = MethodSpec .methodBuilder ("sumOfTen") .addParameter (int.class, "number") .addParameter (strings) .addModifiers (Modifier.PUBLIC, Modifier.STATIC) .addCode (sumOfTenImpl) .build ();

Slik ser den genererte Java-koden ut:

offentlig statisk ugyldig sumOfTen (int-nummer, Liststrenger) {int sum = 0; for (int i = 0; i <= 10; i ++) {sum + = i; }}

7. Type spesifikasjon

Etter å ha utforsket måtene å generere metoder, felt og parametere, la oss ta en titt på typespesifikasjonene.

For å erklære en type, kan vi bruke Type Spesifik som kan bygge klasser, grensesnitt og opplistede typer.

7.1. Generere en klasse

For å generere en klasse, kan vi bruke classBuilder () metoden for Type Spesifik klasse.

Vi kan også spesifisere modifikatorene, for eksempel offentlig og endelig tilgangsmodifikatorer. I tillegg til klassemodifiserere, kan vi også spesifisere felt og metoder ved hjelp av allerede nevnte FieldSpec og MethodSpec klasser.

Noter det addField () og addMethod () metoder er også tilgjengelige når du genererer grensesnitt eller anonyme indre klasser.

La oss ta en titt på følgende klassebyggereksempel:

TypeSpec person = TypeSpec .classBuilder ("Person") .addModifiers (Modifier.PUBLIC) .addField (name) .addMethod (MethodSpec .methodBuilder ("getName") .addModifiers (Modifier.PUBLIC) .returns (String.class) .addStatement ("returner dette.navn") .build ()) .addMethod (MethodSpec .methodBuilder ("setName") .addParameter (String.class, "name") .addModifiers (Modifier.PUBLIC) .returns (String.class). addStatement ("this.name = name") .build ()) .addMethod (sumOfTen) .build ();

Og slik ser den genererte koden ut:

offentlig klasse Person {private Strengnavn; public String getName () {return this.name; } offentlig strengsettnavn (strengnavn) {dette.navn = navn; } offentlig statisk ugyldig sumOfTen (int-nummer, Liststrenger) {int sum = 0; for (int i = 0; i <= 10; i ++) {sum + = i; }}}

7.2. Generere et grensesnitt

For å generere et Java-grensesnitt bruker vi interfaceBuilder () metoden for Type Spesifik.

Vi kan også definere en standardmetode ved å spesifisere MISLIGHOLDE modifikasjonsverdi i addModifiers ():

TypeSpec person = TypeSpec .interfaceBuilder ("Person") .addModifiers (Modifier.PUBLIC) .addField (defaultName) .addMethod (MethodSpec .methodBuilder ("getName") .addModifiers (Modifier.PUBLIC, Modifier.ABSTRACT) .build ()) .addMethod (MethodSpec .methodBuilder ("getDefaultName") .addModifiers (Modifier.PUBLIC, Modifier.DEFAULT) .addCode (CodeBlock .builder () .addStatement ("return DEFAULT_NAME") .build ()) .build ()) .build ();

Den vil generere følgende Java-kode:

offentlig grensesnitt Person {private static final String DEFAULT_NAME = "Alice"; ugyldig getName (); standard ugyldig getDefaultName () {return DEFAULT_NAME; }}

7.3. Generere en Enum

For å generere en nummerert type kan vi bruke enumBuilder () metoden for Type Spesifik. For å spesifisere hver opptalt verdi kan vi ringe addEnumConstant () metode:

TypeSpec gender = TypeSpec .enumBuilder ("Gender") .addModifiers (Modifier.PUBLIC) .addEnumConstant ("MALE") .addEnumConstant ("FEMALE") .addEnumConstant ("UNSPECIFIED") .build ();

Resultatet av de nevnte enumBuilder () logikken er:

offentlig enum Kjønn {MALE, KVINNE, USPESIFISERT}

7.4. Generere en anonym indre klasse

For å generere en anonym indre klasse kan vi bruke anonymousClassBuilder () metoden for Type Spesifik klasse. Noter det vi må spesifisere foreldreklassen i addSuperinterface () metode. Ellers vil den bruke standard foreldreklasse, som er Gjenstand:

TypeSpec-komparator = TypeSpec .anonymousClassBuilder ("") .addSuperinterface (ParameterizedTypeName.get (Comparator.class, String.class)) .addMethod (MethodSpec .methodBuilder ("sammenlign") .addModifiers (Modifier.PUBLIC) .addParameter ("sammenlign"). , "a") .addParameter (String.class, "b") .returns (int.class) .addStatement ("return a.length () - b.length ()") .build ()) .build () ;

Dette vil generere følgende Java-kode:

new Comparator () {public int compare (String a, String b) {return a.length () - b.length (); }});

8. Merknadsspesifikasjon

For å legge til en kommentar til generert kode, kan vi ringe addAnnotation () metode i en MethodSpec eller FieldSpec byggherreklasse:

MethodSpec sumOfTen = MethodSpec .methodBuilder ("sumOfTen") .addAnnotation (Override.class) .addParameter (int.class, "number") .addParameter (strings) .addModifiers (Modifier.PUBLIC, Modifier.STATIC) .addCodeI sumMo) .bygge();

Som genererer:

@Override public static void sumOfTen (int number, List strings) {int sum = 0; for (int i = 0; i <= 10; i ++) {sum + = i; }}

I tilfelle vi trenger å spesifisere medlemsverdien, kan vi ringe addMember () metoden for Kommentar Spes klasse:

AnnotationSpec toString = AnnotationSpec .builder (ToString.class) .addMember ("ekskluder", "\" navn \ "") .build ();

Dette vil generere følgende kommentar:

@ToString (ekskluder = "navn")

9. Generere Javadocs

Javadoc kan genereres ved hjelp av CodeBlock, eller ved å spesifisere verdien direkte:

MethodSpec sumOfTen = MethodSpec .methodBuilder ("sumOfTen") .addJavadoc (CodeBlock .builder () .add ("Summen av alle heltall fra 0 til 10") .build ()) .addAnnotation (Override.class) .addParameter (int. klasse, "nummer") .addParameter (strenger) .addModifiers (Modifier.PUBLIC, Modifier.STATIC) .addCode (sumOfTenImpl) .build ();

Dette vil generere følgende Java-kode:

/ ** * Summen av alle heltall fra 0 til 10 * / @ Overstyr offentlig statisk tomrom sumOfTen (int-nummer, Liststrenger) {int sum = 0; for (int i = 0; i <= 10; i ++) {sum + = i; }}

10. Formatering

La oss sjekke eksemplet på FieldSpec initialisering i seksjon 5 som inneholder en rømningsrøye som brukes til å unnslippe “Alice” String verdi:

initializer ("\" Alice \ "")

Det er også et lignende eksempel i avsnitt 8 når vi definerer det ekskluderte medlemmet av en kommentar:

addMember ("ekskluder", "\" navn \ "")

Det blir uhåndterlig når JavaPoet-koden vår vokser og har mye lignende String unnslippe eller String sammenhengende uttalelser.

Stringformateringsfunksjonen i JavaPoet gjør String formatering i beginControlFlow (), addStatement () eller initializer () metoder enklere. Syntaksen ligner på String.format () funksjonalitet i Java. Det kan hjelpe å formatere bokstavene, strenger, typer og navn.

10.1. Bokstavelig formatering

JavaPoet erstatter $ L. med en bokstavelig verdi i utgangen. Vi kan spesifisere hvilken som helst primitiv type og String verdier i argumentene:

private MethodSpec generereSumMethod (strengnavn, int fra, int til, strengoperator) {return MethodSpec .methodBuilder (navn) .returns (int.class) .addStatement ("int sum = 0") .beginControlFlow ("for (int i = $ L; i <= $ L; i ++) ", fra, til) .addStatement (" sum = sum $ L i ", operator). EndControlFlow () .addStatement (" return sum ") .build (); }

I tilfelle vi kaller createSumMethod () med følgende verdier spesifisert:

createSumMethod ("sumOfOneHundred", 0, 100, "+");

JavaPoet vil generere følgende utdata:

int sumOfOneHundred () {int sum = 0; for (int i = 0; i <= 100; i ++) {sum = sum + i; } retur sum; }

10.2. String Formatering

String formatering genererer en verdi med anførselstegnet, som kun refererer til String skriv inn Java. JavaPoet erstatter $ S med en String verdi i utdata:

privat statisk MethodSpec generateStringSupplier (String methodName, String fieldName) {return MethodSpec .methodBuilder (methodName) .returns (String.class) .addStatement ("return $ S", fieldName) .build (); }

I tilfelle vi kaller createGetter () metode og gi disse verdiene:

createStringSupplier ("getDefaultName", "Bob");

Vi får følgende genererte Java-kode:

Streng getDefaultName () {returner "Bob"; }

10.3. Skriv inn formatering

JavaPoet erstatter $ T med en type i den genererte Java-koden. JavaPoet håndterer typen i importuttalelsen automatisk. Hvis vi hadde gitt typen som bokstavelig i stedet, ville ikke JavaPoet håndtere importen.

MethodSpec getCurrentDateMethod = MethodSpec .methodBuilder ("getCurrentDate") .returns (Date.class) .addStatement ("return new $ T ()", Date.class) .build ();

JavaPoet vil generere følgende utdata:

Date getCurrentDate () {return new Date (); }

10.4. Formatering av navn

I tilfelle vi trenger det referer til et navn på en variabel / parameter, felt eller metode, vi kan bruke $ N i JavaPoet String formatering.

Vi kan legge til forrige getCurrentDateMethod () til den nye referansemetoden:

MethodSpec dateToString = MethodSpec .methodBuilder ("getCurrentDateAsString") .returns (String.class) .addStatement ("$ T formatter = new $ T ($ S)", DateFormat.class, SimpleDateFormat.class, "MM / dd / yyyy HH : mm: ss ") .addStatement (" return formatter.format ($ N ()) ", getCurrentDateMethod) .build ();

Som genererer:

String getCurrentDateAsString () {DateFormat formatter = new SimpleDateFormat ("MM / dd / åååå HH: mm: ss"); return formatter.format (getCurrentDate ()); }

11. Generere Lambda-uttrykk

Vi kan bruke funksjonene som vi allerede har utforsket for å generere et Lambda-uttrykk. For eksempel en kodeblokk som skriver ut Navn felt eller en variabel flere ganger:

CodeBlock printNameMultipleTimes = CodeBlock .builder () .addStatement ("$ T names = new $ T ()", List.class, String.class, ArrayList.class) .addStatement ("$ T.range ($ L, $ L) .forEach (i -> names.add (name)) ", IntStream.class, 0, 10) .addStatement (" names.forEach (System.out :: println) ") .build ();

Den logikken genererer følgende utdata:

Listenavn = ny ArrayList (); IntStream.range (0, 10) .forEach (i -> names.add (name)); names.forEach (System.out :: println);

12. Produsere produksjonen ved hjelp av JavaFile

De JavaFile klasse hjelper til med å konfigurere og produsere utdataene fra den genererte koden. For å generere Java-kode bygger vi bare JavaFile, oppgi pakkenavnet og en forekomst av Type Spesifik gjenstand.

12.1. Kodeinnrykk

Som standard bruker JavaPoet to mellomrom for innrykk. For å holde konsistensen ble alle eksemplene i denne opplæringen presentert med 4 mellomrom innrykk, som er konfigurert via innrykk () metode:

JavaFile javaFile = JavaFile .builder ("com.baeldung.javapoet.person", person) .indent ("") .build ();

12.2. Statisk import

I tilfelle vi trenger å legge til en statisk import, kan vi definere typen og spesifikk metodenavn i JavaFile ved å ringe addStaticImport () metode:

JavaFile javaFile = JavaFile .builder ("com.baeldung.javapoet.person", person) .indent ("") .addStaticImport (Date.class, "UTC") .addStaticImport (ClassName.get ("java.time", " ZonedDateTime ")," * ") .build ();

Som genererer følgende statiske importuttalelser:

importer statisk java.util.Date.UTC; importer statisk java.time.ZonedDateTime. *;

12.3. Produksjon

De skrive til() metoden gir funksjonalitet for å skrive koden i flere mål, for eksempel standard utgangsstrøm (System.out) og Fil.

For å skrive Java-kode til en standard utgangsstrøm, kaller vi bare skrive til() metode, og gi System.out som argumentet:

JavaFile javaFile = JavaFile .builder ("com.baeldung.javapoet.person", person) .indent ("") .addStaticImport (Date.class, "UTC") .addStaticImport (ClassName.get ("java.time", " ZonedDateTime ")," * ") .build (); javaFile.writeTo (System.out);

De skrive til() metoden godtar også java.nio.file. sti og java.io. fil. Vi kan tilby det tilsvarende Sti eller Fil objekt for å generere Java-kildekodefilen i målmappen / banen:

Sti bane = Paths.get (destinationPath); javaFile.writeTo (sti);

For mer detaljert informasjon om JavaFile, se Javadoc.

13. Konklusjon

Denne artikkelen har vært en introduksjon til JavaPoet-funksjoner, som generering av metoder, felt, parametere, typer, merknader og Javadocs.

JavaPoet er kun designet for kodegenerering. I tilfelle vi ønsker å gjøre metaprogrammering med Java, støtter JavaPoet fra versjon 1.10.0 ikke kodekompilering og kjøring.

Som alltid er eksemplene og kodebitene tilgjengelige på GitHub.


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