Introduktion til JavaPoet

1. Oversigt

I denne vejledning undersøger vi de grundlæggende funktioner i JavaPoet-biblioteket.

JavaPoet er udviklet af Square, som leverer API'er til at generere Java-kildekode. Det kan generere primitive typer, referencetyper og deres varianter (såsom klasser, grænseflader, opregnede typer, anonyme indre klasser), felter, metoder, parametre, annoteringer og Javadocs.

JavaPoet administrerer automatisk importen af ​​de afhængige klasser. Det bruger også Builder-mønsteret til at specificere logikken til generering af Java-kode.

2. Maven-afhængighed

For at bruge JavaPoet kan vi downloade den nyeste JAR-fil direkte eller definere følgende afhængighed i vores pom.xml:

 com.squareup javapoet 1.10.0 

3. Metodespecifikation

Lad os først gennemgå metodespecifikationen. For at generere en metode kalder vi simpelthen methodBuilder () metode til MetodeSpec klasse. Vi specificerer det genererede metode navn som en Snor argument af methodBuilder () metode.

Vi kan generere et enkelt logisk udsagn, der slutter med semikolon bruger addStatement () metode. I mellemtiden kan vi definere en kontrolstrøm afgrænset med krøllede parenteser, f.eks hvis ellers blokere eller til løkke, i en kontrolstrøm.

Her er et hurtigt eksempel - generering af sumOfTen () metode, der beregner summen af ​​tal 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 giver følgende output:

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

4. Kodeblokering

Det kan vi også pakke en eller flere kontrolstrømme og logiske udsagn ind i en kodeblok:

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

Hvilket genererer:

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

Vi kan forenkle den tidligere logik i MetodeSpec ved at ringe tilføj kode () og leverer sumOfTenImpl objekt:

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

En kodeblok kan også anvendes til andre specifikationer, såsom typer og Javadocs.

5. Feltspecifikation

Næste - lad os udforske feltspecifikationslogikken.

For at generere et felt bruger vi Bygger() metode til FieldSpec klasse:

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

Dette genererer følgende felt:

privat strengnavn;

Vi kan også initialisere standardværdien af ​​et felt ved at ringe til initialisering () metode:

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

Hvilket genererer:

privat statisk endelig String DEFAULT_NAME = "Alice";

6. Parameter Specifikation

Lad os nu undersøge parameterspecifikationslogikken.

Hvis vi vil tilføje en parameter til metoden, kan vi kalde addParameter () inden for funktionskæden kalder bygherren op.

I tilfælde af mere komplekse parametertyper kan vi gøre brug af ParameterSpec Bygger:

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

Vi kan også tilføje modifikatoren for metoden, f.eks offentlig og / eller statisk:

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

Sådan ser den genererede Java-kode ud:

offentlig statisk ugyldig sumOfTen (int antal, liste strenge) {int sum = 0; for (int i = 0; i <= 10; i ++) {sum + = i; }}

7. Type Specifikation

Efter at have undersøgt måderne til at generere metoder, felter og parametre, lad os nu se på typespecifikationerne.

For at erklære en type kan vi bruge TypeSpec som kan bygge klasser, grænseflader og opregnede typer.

7.1. Generere en klasse

For at generere en klasse kan vi bruge classBuilder () metode til TypeSpec klasse.

Vi kan også specificere dets modifikatorer, for eksempel offentlig og endelig adgangsmodifikatorer. Foruden klassemodifikatorer kan vi også specificere felter og metoder ved hjælp af allerede nævnte FieldSpec og MetodeSpec klasser.

Noter det addField () og addMethod () metoder er også tilgængelige, når der genereres grænseflader eller anonyme indre klasser.

Lad os kigge 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 ("dette.navn = navn") .build ()) .addMethod (sumOfTen) .build ();

Og sådan ser den genererede kode ud:

offentlig klasse person {privat strengnavn; public String getName () {returner dette.navn; } offentlig streng sætnavn (streng navn) {dette.navn = navn; } offentlig statisk ugyldig sumOfTen (int nummer, liste strenge) {int sum = 0; for (int i = 0; i <= 10; i ++) {sum + = i; }}}

7.2. Generering af et interface

For at generere en Java-grænseflade bruger vi interfaceBuilder () metode til Type specifikation.

Vi kan også definere en standardmetode ved at specificere STANDARD ændringsværdi 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 ();

Det genererer følgende Java-kode:

offentlig grænseflade Person {privat statisk endelig String DEFAULT_NAME = "Alice"; ugyldigt getName (); standard ugyldigt getDefaultName () {returner DEFAULT_NAME; }}

7.3. Generere en Enum

For at generere en opregnet type kan vi bruge enumBuilder () metode til Type specifikation. For at specificere hver opregnet værdi kan vi kalde addEnumConstant () metode:

TypeSpec køn = TypeSpec .enumBuilder ("Køn") .addModifiers (Modifier.PUBLIC) .addEnumConstant ("MALE") .addEnumConstant ("FEMALE") .addEnumConstant ("UNSPECIFIED") .build ();

Output af ovennævnte enumBuilder () logik er:

offentlig enum Køn {MALE, KVINNE, USPECIFICERET}

7.4. Generering af en anonym indre klasse

For at generere en anonym indre klasse kan vi bruge anonymousClassBuilder () metode til Type specifikation klasse. Noter det vi skal angive overordnede klasse i addSuperinterface () metode. Ellers bruger den standardforældreklassen, som er Objekt:

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

Dette genererer følgende Java-kode:

ny komparator () {public int sammenligne (streng a, streng b) {returnere a.length () - b.length (); }});

8. Annotationsspecifikation

For at tilføje en kommentar til genereret kode kan vi ringe til addAnnotation () metode i en MetodeSpec eller FieldSpec bygherre klasse:

MethodSpec sumOfTen = MethodSpec .methodBuilder ("sumOfTen") .addAnnotation (Override.class) .addParameter (int.class, "number") .addParameter (strings) .addModifiers (Modifier.PUBLIC, Modifier.STATIC) .addCenI (sumi) .build ();

Hvilket genererer:

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

Hvis vi har brug for at specificere medlemsværdien, kan vi ringe til tilføjMedlem () metode til AnnotationSpec klasse:

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

Dette genererer følgende kommentar:

@ToString (ekskluder = "navn")

9. Generering af Javadocs

Javadoc kan genereres ved hjælp af CodeBlock, eller ved at angive værdien direkte:

MethodSpec sumOfTen = MethodSpec .methodBuilder ("sumOfTen") .addJavadoc (CodeBlock .builder () .add ("Summen af ​​alle heltal fra 0 til 10") .build ()) .addAnnotation (Override.class) .addParameter (int. klasse, "antal") .addParameter (strenge) .addModifiers (Modifier.PUBLIC, Modifier.STATIC) .addCode (sumOfTenImpl) .build ();

Dette genererer følgende Java-kode:

/ ** * Summen af ​​alle heltal fra 0 til 10 * / @ Overstyr offentlig statisk ugyldig sumOfTen (int-nummer, Listestrenge) {int sum = 0; for (int i = 0; i <= 10; i ++) {sum + = i; }}

10. Formatering

Lad os kontrollere eksemplet på FieldSpec initialisering i afsnit 5, som indeholder en flugttegn, der bruges til at undslippe “Alice” Snor værdi:

initializer ("\" Alice \ "")

Der er også et lignende eksempel i afsnit 8, når vi definerer det ekskluderede medlem af en kommentar:

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

Det bliver uhåndterligt, når vores JavaPoet-kode vokser og har meget lignende Snor flygte eller Snor sammenkædningserklæringer.

Stringformateringsfunktionen i JavaPoet gør Snor formatering i beginControlFlow (), addStatement () eller initialisering () metoder lettere. Syntaksen ligner String.format () funktionalitet i Java. Det kan hjælpe med at formatere bogstaver, strenge, typer og navne.

10.1. Bogstavelig formatering

JavaPoet erstatter $ L. med en bogstavelig værdi i output. Vi kan specificere enhver primitiv type og Snor værdier i argumenterne:

private MethodSpec generateSumMethod (String name, int from, int to, String operator) {return MethodSpec .methodBuilder (name) .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 (); }

Hvis vi kalder createSumMethod () med følgende værdier specificeret:

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

JavaPoet genererer følgende output:

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

10.2. Snor Formatering

Snor formatering genererer en værdi med anførselstegnet, som udelukkende refererer til Snor skriv Java. JavaPoet erstatter $ S med en Snor værdi i output:

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

Hvis vi kalder createGetter () metode og give disse værdier:

createStringSupplier ("getDefaultName", "Bob");

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

Streng getDefaultName () {returner "Bob"; }

10.3. Skriv formatering

JavaPoet erstatter $ T med en type i den genererede Java-kode. JavaPoet håndterer automatisk typen i importerklæringen. Hvis vi i stedet havde angivet typen som en bogstavelig, ville JavaPoet ikke håndtere importen.

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

JavaPoet genererer følgende output:

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

10.4. Formatering af navn

Hvis vi har brug for det henvis til et navn på en variabel / parameter, felt eller metode, vi kan bruge $ N i JavaPoet Snor formatering.

Vi kan tilføje det forrige getCurrentDateMethod () til den nye referencemetode:

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 ();

Hvilket genererer:

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

11. Generering af Lambda-udtryk

Vi kan gøre brug af de funktioner, som vi allerede har undersøgt for at generere et Lambda-udtryk. For eksempel en kodeblok, der udskriver navn felt eller en variabel flere gange:

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 ();

Denne logik genererer følgende output:

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

12. Fremstilling af output ved hjælp af JavaFile

Det JavaFile klasse hjælper med at konfigurere og producere output fra den genererede kode. For at generere Java-kode bygger vi simpelthen JavaFile, angiv pakkens navn og en forekomst af TypeSpec objekt.

12.1. Kodeindrykning

Som standard bruger JavaPoet to mellemrum til indrykning. For at opretholde konsistensen blev alle eksempler i denne vejledning præsenteret med 4 rumindrykk, som er konfigureret via indrykning () metode:

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

12.2. Statisk import

Hvis vi har brug for at tilføje en statisk import, kan vi definere typen og navnet på den specifikke metode i JavaFile ved at ringe til addStaticImport () metode:

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

Hvilket genererer følgende statiske importerklæringer:

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

12.3. Produktion

Det skrive til() metode giver funktionalitet til at skrive koden i flere mål, såsom standard output stream (System.out) og Fil.

For at skrive Java-kode til en standard outputstream kalder vi simpelthen skrive til() metode og give den 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);

Det skrive til() metode accepterer også java.nio.file.Path og java.io-fil. Vi kan levere det tilsvarende Sti eller Fil objekt for at generere Java-kildekodefilen i destinationsmappen / stien:

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

For mere detaljeret information om JavaFile, se venligst Javadoc.

13. Konklusion

Denne artikel har været en introduktion til JavaPoet-funktionaliteter, som genereringsmetoder, felter, parametre, typer, annoteringer og Javadocs.

JavaPoet er kun designet til kodegenerering. Hvis vi gerne vil foretage metaprogrammering med Java, understøtter JavaPoet fra version 1.10.0 ikke kodekompilering og kørsel.

Som altid er eksemplerne og kodestykkerne tilgængelige på GitHub.


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