Inleiding tot JavaPoet

1. Overzicht

In deze tutorial verkennen we de basisfunctionaliteiten van de JavaPoet-bibliotheek.

JavaPoet is ontwikkeld door Square, die biedt API's om Java-broncode te genereren. Het kan primitieve typen, referentietypes en hun varianten (zoals klassen, interfaces, opgesomde typen, anonieme innerlijke klassen), velden, methoden, parameters, annotaties en Javadocs genereren.

JavaPoet beheert automatisch de import van de afhankelijke klassen. Het gebruikt ook het Builder-patroon om de logica te specificeren om Java-code te genereren.

2. Maven Afhankelijkheid

Om JavaPoet te gebruiken, kunnen we direct het nieuwste JAR-bestand downloaden of de volgende afhankelijkheid definiëren in onze pom.xml:

 com.squareup javapoet 1.10.0 

3. Methode specificatie

Laten we eerst de methodespecificatie doornemen. Om een ​​methode te genereren, noemen we eenvoudig de methodBuilder () methode van MethodSpec klasse. We specificeren de gegenereerde methode naam als een Draad argument van de methodBuilder () methode.

Wij kunnen genereer een enkele logische instructie die eindigt op de puntkomma de ... gebruiken addStatement () methode. Ondertussen kunnen we één besturingsstroom definiëren die wordt begrensd door accolades, zoals als-anders blok, of voor lus, in een controlestroom.

Hier is een snel voorbeeld: het genereren van de sumOfTen () methode die de som van getallen van 0 tot 10 berekent:

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

Dit levert de volgende output op:

leegte sumOfTen () {int sum = 0; voor (int i = 0; i <= 10; i ++) {som + = i; }}

4. Codeblok

We kunnen ook wikkel een of meer controlestromen en logische instructies in één codeblok:

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

Die genereert:

int som = 0; voor (int i = 0; i <= 10; i ++) {som + = i; }

We kunnen de eerdere logica in het MethodSpec door te bellen addCode () en het verstrekken van de sumOfTenImpl voorwerp:

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

Een codeblok is ook toepasbaar op andere specificaties, zoals types en Javadocs.

5. Veldspecificatie

Vervolgens - laten we de logica voor veldspecificatie onderzoeken.

Om een ​​veld te genereren, gebruiken we de bouwer() methode van de FieldSpec klasse:

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

Dit genereert het volgende veld:

private String naam;

We kunnen ook de standaardwaarde van een veld initialiseren door de initializer () methode:

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

Die genereert:

private static final String DEFAULT_NAME = "Alice";

6. Parameter Specificatie

Laten we nu de logica van de parameterspecificatie onderzoeken.

Als we een parameter aan de methode willen toevoegen, kunnen we de addParameter () binnen de keten van de functie-aanroepen in de builder.

Bij complexere parametertypes kunnen we gebruik maken van ParameterSpec bouwer:

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

We kunnen ook de modifier van de methode toevoegen, zoals openbaar en / of statisch:

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

Zo ziet de gegenereerde Java-code eruit:

openbare statische ongeldige sumOfTen (int nummer, lijststrings) {int sum = 0; voor (int i = 0; i <= 10; i ++) {som + = i; }}

7. Typespecificatie

Nadat we de manieren hebben verkend om methoden, velden en parameters te genereren, gaan we nu eens kijken naar de typespecificaties.

Om een ​​type aan te geven, kunnen we de TypeSpec die klassen, interfaces en opgesomde typen kunnen bouwen.

7.1. Een klas genereren

Om een ​​klasse te genereren, kunnen we de classBuilder () methode van de TypeSpec klasse.

We kunnen ook de modificatoren specificeren, bijvoorbeeld openbaar en laatste toegang tot modificatoren. Naast class-modifiers kunnen we ook velden en methoden specificeren met behulp van de reeds genoemde FieldSpec en MethodSpec klassen.

Let daar op veld toevoegen() en addMethod () methoden zijn ook beschikbaar bij het genereren van interfaces of anonieme innerlijke klassen.

Laten we eens kijken naar het volgende voorbeeld van de klassenbouwer:

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

En zo ziet de gegenereerde code eruit:

openbare klasse Persoon {privé Stringnaam; public String getName () {retourneer this.name; } public String setName (String naam) {this.name = naam; } openbare statische ongeldige sumOfTen (int nummer, lijst strings) {int sum = 0; voor (int i = 0; i <= 10; i ++) {som + = i; }}}

7.2. Een interface genereren

Om een ​​Java-interface te genereren, gebruiken we de interfaceBuilder () methode van de TypeSpec.

We kunnen ook een standaardmethode definiëren door op te geven STANDAARD modifier-waarde in de addModifiers ():

TypeSpec person = TypeSpec .interfaceBuilder ("Persoon") .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 ();

Het genereert de volgende Java-code:

openbare interface Persoon {privé statische laatste String DEFAULT_NAME = "Alice"; ongeldig getName (); standaard ongeldig getDefaultName () {retourneer DEFAULT_NAME; }}

7.3. Een Enum genereren

Om een ​​opgesomd type te genereren, kunnen we de enumBuilder () methode van de TypeSpec. Om elke opgesomde waarde te specificeren, kunnen we de addEnumConstant () methode:

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

De output van het bovengenoemde enumBuilder () logica is:

openbare opsomming Geslacht {MALE, FEMALE, UNSPECIFIED}

7.4. Een anonieme innerlijke klasse genereren

Om een ​​anonieme inner class te genereren, kunnen we de anonymousClassBuilder () methode van de TypeSpec klasse. Let daar op we moeten de bovenliggende klasse specificeren in de addSuperinterface () methode. Anders wordt de standaard bovenliggende klasse gebruikt, namelijk Voorwerp:

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

Dit genereert de volgende Java-code:

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

8. Annotatiespecificatie

Om een ​​annotatie aan de gegenereerde code toe te voegen, kunnen we de addAnnotation () methode in een MethodSpec of FieldSpec bouwer klasse:

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

Die genereert:

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

Als we de lidwaarde moeten specificeren, kunnen we de lid toevoegen() methode van de Annotatiespec klasse:

AnnotationSpec toString = AnnotationSpec .builder (ToString.class) .addMember ("exclude", "\" name \ "") .build ();

Dit genereert de volgende annotatie:

@ToString (exclude = "name")

9. Javadocs genereren

Javadoc kan worden gegenereerd met CodeBlock, of door de waarde rechtstreeks op te geven:

MethodSpec sumOfTen = MethodSpec .methodBuilder ("sumOfTen") .addJavadoc (CodeBlock .builder () .add ("Som van alle gehele getallen van 0 tot 10") .build ()) .addAnnotation (Override.class) .addParameter (int. class, "number") .addParameter (strings) .addModifiers (Modifier.PUBLIC, Modifier.STATIC) .addCode (sumOfTenImpl) .build ();

Dit genereert de volgende Java-code:

/ ** * Som van alle gehele getallen van 0 tot 10 * / @Override public static void sumOfTen (int number, List strings) {int sum = 0; voor (int i = 0; i <= 10; i ++) {som + = i; }}

10. Formatteren

Laten we het voorbeeld van de FieldSpec initializer in sectie 5 die een escape-teken bevat dat wordt gebruikt om de "Alice" te ontsnappen Draad waarde:

initializer ("\" Alice \ "")

Er is ook een soortgelijk voorbeeld in sectie 8 wanneer we het uitgesloten lid van een annotatie definiëren:

addMember ("uitsluiten", "\" naam \ "")

Het wordt onpraktisch wanneer onze JavaPoet-code groeit en veel gelijkenissen vertoont Draad ontsnappen of Draad aaneenschakelingsverklaringen.

De tekenreeksopmaakfunctie in JavaPoet maakt Draad formatteren in beginControlFlow (), addStatement () of initializer () methoden gemakkelijker. De syntaxis is vergelijkbaar met String.format () functionaliteit in Java. Het kan helpen om letterlijke tekens, tekenreeksen, typen en namen op te maken.

10.1. Letterlijke opmaak

JavaPoet vervangt $ L met een letterlijke waarde in de uitvoer. We kunnen elk primitief type specificeren en Draad waarden in het argument (en):

private MethodSpec genererenSumMethod (String naam, int from, int to, String operator) {return MethodSpec .methodBuilder (naam) .returns (int.class) .addStatement ("int sum = 0") .beginControlFlow ("for (int i = $ L; i <= $ L; i ++) ", van, tot) .addStatement (" som = som $ L i ", operator) .endControlFlow () .addStatement (" retourbedrag ") .build (); }

Voor het geval we de genererenSumMethod () met de volgende waarden gespecificeerd:

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

JavaPoet genereert de volgende uitvoer:

int sumOfOneHundred () {int sum = 0; voor (int i = 0; i <= 100; i ++) {som = som + i; } retourbedrag; }

10.2. Draad Formatteren

Draad opmaak genereert een waarde met het aanhalingsteken, die uitsluitend verwijst naar Draad typ Java. JavaPoet vervangt $ S met een Draad waarde in de output:

private static MethodSpec generationStringSupplier (String methodName, String fieldName) {return MethodSpec .methodBuilder (methodName) .returns (String.class) .addStatement ("return $ S", fieldName) .build (); }

Voor het geval we de genererenGetter () methode en geef deze waarden op:

genererenStringSupplier ("getDefaultName", "Bob");

We krijgen de volgende gegenereerde Java-code:

String getDefaultName () {retourneer "Bob"; }

10.3. Typ opmaak

JavaPoet vervangt $ T met een type in de gegenereerde Java-code. JavaPoet verwerkt het type in de importinstructie automatisch. Als we het type in plaats daarvan letterlijk hadden opgegeven, zou JavaPoet de import niet afhandelen.

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

JavaPoet genereert de volgende uitvoer:

Datum getCurrentDate () {retourneer nieuwe datum (); }

10.4. Naamopmaak

Voor het geval dat nodig is verwijzen naar een naam van een variabele / parameter, veld of methode die we kunnen gebruiken $ N in JavaPoet's Draad formatter.

We kunnen de vorige toevoegen getCurrentDateMethod () naar de nieuwe verwijzingsmethode:

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

Die genereert:

String getCurrentDateAsString () {DateFormat-formatter = nieuwe SimpleDateFormat ("MM / dd / jjjj HH: mm: ss"); retourneer formatter.format (getCurrentDate ()); }

11. Lambda-expressies genereren

We kunnen gebruikmaken van de functies die we al hebben onderzocht om een ​​Lambda-expressie te genereren. Bijvoorbeeld een codeblok dat de naam veld of een variabele meerdere keren:

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

Die logica genereert de volgende output:

Lijstnamen = nieuwe ArrayList (); IntStream.range (0, 10) .forEach (i -> names.add (naam)); names.forEach (System.out :: println);

12. Het produceren van de uitvoer met JavaFile

De JavaFile class helpt bij het configureren en produceren van de output van de gegenereerde code. Om Java-code te genereren, bouwen we eenvoudigweg het JavaFile, geef de pakketnaam en een instantie van de TypeSpec voorwerp.

12.1. Code inspringen

Standaard gebruikt JavaPoet twee spaties voor inspringen. Om de consistentie te behouden, werden alle voorbeelden in deze tutorial gepresenteerd met 4 spaties-inspringing, die wordt geconfigureerd via inspringen() methode:

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

12.2. Statische invoer

In het geval dat we een statische import moeten toevoegen, kunnen we het type en de specifieke methode naam in het JavaFile door het addStaticImport () methode:

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

Dit genereert de volgende statische importinstructies:

importeer statische java.util.Date.UTC; importeer statische java.time.ZonedDateTime. *;

12.3. Uitvoer

De Schrijven aan() methode biedt functionaliteit om de code in meerdere doelen te schrijven, zoals standaard uitvoerstroom (System.out) en het dossier.

Om Java-code naar een standaard uitvoerstroom te schrijven, noemen we eenvoudig de Schrijven aan() methode en geef de System.out als argument:

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

De Schrijven aan() methode accepteert ook java.nio.file.Path en java.io.File. Wij kunnen de bijbehorende leveren Pad of het dossier object om het Java-broncodebestand in de bestemmingsmap / -pad te genereren:

Padpad = Paths.get (bestemmingspad); javaFile.writeTo (pad);

Voor meer gedetailleerde informatie over JavaFile, verwijzen wij u naar de Javadoc.

13. Conclusie

Dit artikel is een inleiding tot JavaPoet-functionaliteiten, zoals het genereren van methoden, velden, parameters, typen, annotaties en Javadocs.

JavaPoet is alleen ontworpen voor het genereren van code. In het geval dat we metaprogrammering met Java zouden willen doen, ondersteunt JavaPoet vanaf versie 1.10.0 geen codecompilatie en-running.

Zoals altijd zijn de voorbeelden en codefragmenten beschikbaar op GitHub.


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