源码地址: LAnnotationProcessor
JavaPoet定义了一系列类来尽可能优雅的描述java源文件的结构。观察JavaPoet的代码主要的类
可以分为以下几种:
addModifiers(Modifier... modifiers)
Modifier是一个枚举对象,枚举值为修饰关键字Public、Protected、Private、Static、Final
等。
所有在JavaPoet创建的对象都必须设置修饰符(包括方法、类、接口、枚举、参数、变量)。
addAnnotation(AnnotationSpec annotationSpec)
addAnnotation(ClassName annotation)
addAnnotation(Class> annotation)
addAnnotations(java.lang.Iterable annotationSpecs)
该方法即为类或方法或参数设置注解,参数即可以是AnnotationSpec,也可以是ClassName,也可以直接传递Class对象,甚至可以是调用addAnnotations
直接传递一个AnnotationSpec列表
。一般情况下,包含复杂属性的注解一般用AnnotationSpec,如果单纯添加基本注解,无其他附加属性可以直接使用ClassName
或者Class
即可。
addField(FieldSpec fieldSpec)
addField(TypeName type, java.lang.String name, javax.lang.model.element.Modifier... modifiers)
addField(java.lang.reflect.Type type, java.lang.String name, javax.lang.model.element.Modifier... modifiers)
addFields(java.lang.Iterable fieldSpecs)
该方法为类或接口设置字段,参数可以是FieldSpec,也可以通过指定字段的类型、修饰符及名称,或者通过调用addFields
传递多个参数。
addJavadoc(CodeBlock block)
addJavadoc(java.lang.String format, java.lang.Object... args)
在编写类、方法、成员变量时,可以通过addJavadoc来设置注释,可以直接传入String对象,或者传入CodeBlock(代码块)。
JavaPoet的大多数API都使用普通的旧的不可变Java对象。还有构建器,方法链和varargs来使AP
I友好。 JavaPoet为类和接口(TypeSpec
),字段(FieldSpec
),方法和构造函数(MethodSpe c
),参数(ParameterSpec
)和注解(AnnotationSpec
)提供模型。但是方法和构造函数的主体并未建模。 没有表达式类,没有语句类或语法树节点。为了简化繁琐的手动分号,换行和缩进,JavaPoet提供简便的了API:
addMethod(MethodSpec methodSpec)
addMethods(java.lang.Iterable methodSpecs)
在编写类/接口时,通过addMethod()
或addMethods()
添加方法。
TypeSpec
为生成的类,接口或枚举声明。
获取TypeSpec.Builder:
TypeSpec.Builder的API
addAnnotation
:设置注解addEnumConstant
:设置枚举值(仅用于枚举类)addField
:设置字段addInitializerBlock
:设置初始化代码块addJavadoc
:设置注释addOriginatingElement
:设置注解处理器的源元素addStaticBlock
:设置静态代码块addSuperinterface
:设置实现的接口
addType
:添加内部类
addTypeVariable
:设置类型变量
superclass
:设置父类
使用classBuilder
创建类。例如:
// 字段:id
FieldSpec id = FieldSpec.builder(int.class, "id", Modifier.PUBLIC).build();
// 初始化代码块
CodeBlock initBlock = CodeBlock.builder()
.add("$N = $L", id, 10)
.build();
// 注释块
CodeBlock docBlock = CodeBlock.builder()
.add("This is the example for generating class")
.build();
// 方法
MethodSpec getIdMeth = MethodSpec.methodBuilder("getId")
.returns(int.class)
.addStatement("return this.$N", id)
.build();
// 静态代码块
CodeBlock staticBlock = CodeBlock.builder()
.add("$T EKY = $S", String.class, "key_tea")
.build();
// 接口
ClassName swimInterface = ClassName.get("com.teaphy.poet.interfaces", "ISwim");
// 超类
ClassName supperClass = ClassName.get("com.teaphy.poet.module", "Student");
// 类型变量
TypeVariableName tTypeVariable = TypeVariableName.get("T");
TypeVariableName vTypeVariable = TypeVariableName.get("V", Number.class);
TypeSpec inner = TypeSpec.classBuilder("Inner").build();
// 使用classBuilder创建类
TypeSpec tea = TypeSpec.classBuilder("Tea")
.addModifiers(Modifier.PUBLIC) // 设置修饰符
.addAnnotation(Author.class) // 设置注解
.addField(id) // 设置字段
.addInitializerBlock(initBlock) // 设置初始化代码块
.addJavadoc(docBlock) // 设置注释
.addMethod(getIdMeth) // 设置方法
.addStaticBlock(staticBlock) // 设置静态代码块
.addSuperinterface(swimInterface) // 设置实现的接口
.superclass(supperClass) // 设置父类
.addTypeVariable(tTypeVariable) // 设置类型变量
.addTypeVariable(vTypeVariable) // 设置类型变量
.addType(inner) // 设置内部类
.build();
生成的类:
package com.teaphy.poet.generated;
import com.teaphy.poet.annotations.Author;
import com.teaphy.poet.interfaces.ISwim;
import com.teaphy.poet.module.Student;
import java.lang.Number;
import java.lang.String;
/**
* This is the example for generating class
* */
@Author
public class Tea extends Student implements ISwim {
static {
String EKY = "key_tea"
}
public int id;
{
id = 10}
int getId() {
return this.id;
}
class Inner {
}
}
使用enumBuilder
创建枚举类,并调用addEnumConstant
添加值。例如:
TypeSpec enumBuilder = TypeSpec.enumBuilder("SexKind")
.addEnumConstant("MAN")
.addEnumConstant("WOMAN")
.build();
JavaFile javaFile = JavaFile.builder("com.teaphy.poet.generated", enumBuilder)
.build();
javaFile.writeTo(System.out);
生成的枚举类:
enum SexKind {
MAN,
WOMAN
}
使用interfaceBuilder
创建接口。
例如:
// 接口中声明的属性必须为被`public static final`
FieldSpec versionField = FieldSpec.builder(int.class, "Version")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)// 属性默认为Public Static Final.
.initializer("$L", 1)
.build();
// 接口中声明的方法必须被[abstract, static, default]的其中之一修饰
MethodSpec abstractMeth = MethodSpec.methodBuilder("swim")
.addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC) // abstract方法必须被[public, private]其中之一修饰
.build();
MethodSpec defaultMeth = MethodSpec.methodBuilder("tintDefault")
.addModifiers(Modifier.DEFAULT, Modifier.PRIVATE)// default方法必须被[public, private]其中之一修饰
.addStatement("$T.out.println($S);", System.class, "This is the example of the default method in the interface")
.build();
MethodSpec staticMeth = MethodSpec.methodBuilder("tintStatic")
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)// static方法必须被[public, private]其中之一修饰
.addStatement("$T.out.println($S);", System.class, "This is the example of the static method in the interface")
.build();
TypeSpec faceBuilder = TypeSpec.interfaceBuilder("ISwim")
.addModifiers(Modifier.PUBLIC) // 必须设置修饰符,public/private,否则报错`java.lang.IllegalArgumentException: modifiers [] must contain one of [public, private]`
.addField(versionField)
.addMethod(abstractMeth)
.addMethod(defaultMeth)
.addMethod(staticMeth)
.build();
生成的接口:
package com.teaphy.poet.generated;
import java.lang.System;
public interface ISwim {
int Version = 1;
void swim();
private default void tintDefault() {
System.out.println("This is the example of the default method in the interface");;
}
static void tintStatic() {
System.out.println("This is the example of the static method in the interface");;
}
}
需要注意的是:
public static final
abstract
(或static
,或default
)方法必须被[public,private]其中之一修饰否则报错java.lang.IllegalArgumentException: modifiers [] must contain one of [public, private]
使用annotationBuilder
创建注解。
例如:
AnnotationSpec annoDoc = AnnotationSpec.builder(Documented.class).build();
AnnotationSpec annoRet = AnnotationSpec.builder(Retention.class)
.addMember("value", "$T.$L", RetentionPolicy.class, RetentionPolicy.RUNTIME)
.build();
AnnotationSpec annoTarget = AnnotationSpec.builder(Target.class)
// $T 标识枚举类, $L 标识 枚举值
.addMember("value", "$T.$L", ElementType.class, ElementType.TYPE)
.addMember("value", "$T.$L", ElementType.class, ElementType.METHOD)
.addMember("value", "$T.$L", ElementType.class, ElementType.FIELD)
.build();
MethodSpec nameMeth = MethodSpec.methodBuilder("name")
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.returns(String.class)
.defaultValue("$S", "tea") // 设置默认值
.build();
TypeSpec authorBuilder = TypeSpec.annotationBuilder("Author")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(annoDoc)
.addAnnotation(annoRet)
.addAnnotation(annoTarget)
.addMethod(nameMeth)
.build();
生成的注解:
import java.lang.annotation.Target;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
ElementType.TYPE,
ElementType.METHOD,
ElementType.FIELD
})
public @interface Author {
String name() default "tea";
}
使用anonymousClassBuilder
创建匿名内部类。
// 匿名内部类实现的接口
ClassName swimInterface = ClassName.get("com.teaphy.poet.interfaces", "ISwim");
// 重写的方法
MethodSpec swimMeth = MethodSpec.methodBuilder("swim")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.build();
// 创建匿名内部类
TypeSpec swimBuilder = TypeSpec.anonymousClassBuilder("")
.addSuperinterface(swimInterface)
.addMethod(swimMeth)
.build();
FieldSpec fieldSwim = FieldSpec.builder(swimInterface, "swim")
.initializer("$L", swimBuilder)
.build();
// 匿名内部类的超类
ClassName frutesClass = ClassName.get("com.teaphy.poet.module", "Fruits");
String nameFru = "Apple";
float priceFru = 10.5f;
// 用于传递参数
CodeBlock pramBlock = CodeBlock.builder()
.add("$S, $L", nameFru, priceFru)
.build();
MethodSpec printDesc = MethodSpec.methodBuilder("printDesc")
.addAnnotation(Override.class)
.addStatement("$T.out.println($T.format(\"the price of the %s is %s\", $N, $N))", System.class,
String.class,
"name",
"price")
.build();
// 创建匿名内部类
TypeSpec fruitBuilder = TypeSpec.anonymousClassBuilder(pramBlock)
.superclass(frutesClass)
.addMethod(printDesc)
.build();
FieldSpec fieldFru = FieldSpec.builder(frutesClass, "fruit")
.initializer("$L", fruitBuilder)
.build();
TypeSpec personBuilder = TypeSpec.classBuilder("Person")
.addField(fieldSwim)
.addField(fieldFru)
.build();
生成的匿名内部类:
package com.teaphy.poet.generated;
import com.teaphy.poet.interfaces.ISwim;
import com.teaphy.poet.module.Fruits;
import java.lang.Override;
import java.lang.String;
import java.lang.System;
class Person {
ISwim swim = new ISwim() {
@Override
public void swim() {
}
};
Fruits fruit = new Fruits("Apple", 10.5) {
@Override
void printDesc() {
System.out.println(String.format("the price of the %s is %s", name, price));
}
};
}
注意:
$L
引用值CodeBlock
语法和逗号分隔参数AnnotationSpec
是声明上生成的注解,可以使用AnnotationSpec.builder()
设置注解的属性
,也可以使用get
方法依据指定的注解的设置属性。
在AnnotationSpec.Builder
中,通过调用addMember()
方法给注解指定的属性设置值。
addMember(java.lang.String name, CodeBlock codeBlock)
addMember(java.lang.String name, java.lang.String format, java.lang.Object... args)
例如:
// 使用`get`方法依据指定的注解的设置属性。
Annotation annoDate = AnnotationSpec_Demo.class.getAnnotation(Date.class);
AnnotationSpec dateSpec = AnnotationSpec.get(annoDate);
// 使用`AnnotationSpec.builder()`设置注解的属性
ClassName annoAuthor = ClassName.get("com.teaphy.poet.annotations", "Author");
CodeBlock typeBlock = CodeBlock.builder()
.add("$S", "B") // 这里不能调用addStatement,因为addStatement会添加分号,与语法不符
.build();
AnnotationSpec authorSpec = AnnotationSpec.builder(annoAuthor)
.addMember("name", "$S", "tea") // addMember设置注解属性
.addMember("type", typeBlock)
.build();
//
ClassName annoAuthors = ClassName.get("com.teaphy.poet.annotations", "Authors");
AnnotationSpec authorsSpec = AnnotationSpec.builder(annoAuthors)
.addMember("value", "$L", authorSpec) // addMember设置注解属性
.addMember("value", "$L", AnnotationSpec.builder(annoAuthor)
.addMember("name", "$S", "teaA") // addMember设置注解属性
.addMember("type", "$S", "C")
.build())
.build();
TypeSpec swimBuilder = TypeSpec.interfaceBuilder("ISwim")
.addAnnotation(dateSpec)
.addAnnotation(authorSpec)
.addAnnotation(authorsSpec)
.build();
生成的代码:
package com.teaphy.poet.generated;
import com.teaphy.poet.annotations.Author;
import com.teaphy.poet.annotations.Authors;
import com.teaphy.poet.annotations.Date;
@Date(
day = "11",
month = "4",
year = "2019"
)
@Author(
name = "tea",
type = "B"
)
@Authors({
@Author(name = "tea", type = "B"),
@Author(name = "teaA", type = "C")
})
interface ISwim {
}
需要注意的是:
$L
进行设置嵌入式注解。addMember()
时,以填充该属性的值列表。addMember()
方法中,通过CodeBlock
设置属性值时,只能调用其add()
方法,而不能调用addStatement()
方法,因为addStatement会添加分号,与语法不符。MethodSpec
是生成构造函数或方法的声明。JavaPoet生成方法分为两种,第一种是构造方法,另一种为常规方法(包括重写方法)。
MethodSpec.constructorBuilder()
MethodSpec.methodBuilder(java.lang.String name)
MethodSpec.overriding
MethodSpec.Builder的API:
addAnnotation
:设置注解addCode
:设置方法体
addComment
:设置单行注释
addJavadoc
:设置文档注释addModifiers
:设置修饰关键字addException
:设置异常
addNamedCode
:设置代码块,通过命名参数方式格式化字符串(详见CodeBock.格式化字符串
)
addParameter
:设置参数
addStatement
:用于处理分号和换行符addTypeVariable
:设置类型变量
ControlFlow
: 用于括号,换行符和缩进
defaultValue
:设置默认值
returns
:设置返回值类型
varargs
:设置最后一个参数是否为可变参数
例如:
// 声明字段:id、name
FieldSpec idField = FieldSpec.builder(int.class, "id").build();
FieldSpec nameField = FieldSpec.builder(String.class, "name").build();
// 构造函数
MethodSpec constructor = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ParameterSpec.builder(int.class, "id").build())
.addParameter(ParameterSpec.builder(String.class, "name").build())
.addStatement("this.$N=$N", idField, "id")
.addStatement("this.$N=$N", nameField, "name")
.build();
// 常规方法
Map map = new HashMap<>();
map.put("id", 1);
map.put("name", "tea");
TypeVariableName t = TypeVariableName.get("T");
TypeName subArray = ArrayTypeName.of(String.class);
MethodSpec doSwim = MethodSpec.methodBuilder("doSwim")
.addModifiers(Modifier.PUBLIC) // 设置修饰关键字
.addAnnotation(Author.class) // 设置注解
.addParameter(ParameterSpec.builder(int.class, "id").build()) // 设置参数
.addParameter(ParameterSpec.builder(String.class, "name").build())
.addCode("$T a = $L", String.class, "A") // 设置方法体,此方法并不添加分号和换行。
.addCode(";")
.addCode("\n")
.addComment("$L", "这是单行注释") // 设置单行注释
.addException(IndexOutOfBoundsException.class) // 设置方法抛出的异常
.addJavadoc("$L", "这里方法的文档注释") // 设置文档注释
.addCode("$T desc = ", String.class)
.addNamedCode("\"id: $id:L, name: $name:L\"", map) // 设置代码块,通过命名参数方式格式化字符串
.addStatement("") // 设置分号/换行符
.addTypeVariable(t) // 设置类型参数变量
.addStatement("$T t", t) // 使用类型参数 声明局部变量
.addStatement("long now = $T.currentTimeMillis()", System.class) // 设置分号、换行和缩进 -> start
.beginControlFlow("if ($T.currentTimeMillis() < now)", System.class)
.addStatement("$T.out.println($S)", System.class, "Time travelling, woo hoo!")
.nextControlFlow("else if ($T.currentTimeMillis() == now)", System.class)
.addStatement("$T.out.println($S)", System.class, "Time stood still!")
.nextControlFlow("else")
.addStatement("$T.out.println($S)", System.class, "Ok, time still moving forward")
.addStatement("$T i = $L", int.class, 0)
.beginControlFlow("do")
.addStatement("i++")
.endControlFlow("while(i < 5)") // 设置分号、换行和缩进 -> end
.returns(t) // 设置返回值类型
.addStatement("return t")
.addParameter(subArray, "subjects")
.varargs() // 设置最后一个参数为可变参数,注意,如果参数为可变参数,其数据类型必须为数组
.build();
TypeSpec person = TypeSpec.classBuilder("Person") // 使用classBuilder创建类
.addField(idField)
.addField(nameField)
.addMethod(constructor)
.addMethod(doSwim)
.build();
生成的方法:
package com.teaphy.poet.generated;
import com.teaphy.poet.annotations.Author;
import java.lang.IndexOutOfBoundsException;
import java.lang.String;
import java.lang.System;
class Person {
int id;
String name;
public Person(int id, String name) {
this.id=id;
this.name=name;
}
/**
* 这里方法的文档注释 */
@Author
public T doSwim(int id, String name, String... subjects) throws IndexOutOfBoundsException {
String a = A;
// 这是单行注释
String desc = "id: 1, name: tea";
T t;
long now = System.currentTimeMillis();
if (System.currentTimeMillis() < now) {
System.out.println("Time travelling, woo hoo!");
} else if (System.currentTimeMillis() == now) {
System.out.println("Time stood still!");
} else {
System.out.println("Ok, time still moving forward");
int i = 0;
do {
i++;
} while(i < 5);
return t;
}
}
}
FieldSpec
是生成的字段的声明。
获取FieldSpec.Builder:
FieldSpec.Builder的API:
addAnnotation
:设置注解addJavadoc
:设置文档注释addModifiers
:设置修饰关键字initializer
: 成员变量的实例化
例如:
// // 创建一个 int 类型 的 成员变量 id,
FieldSpec idField = FieldSpec.builder(int.class, "age")
.build();
// 创建一个成员变量并初始化 (name = "AAA")
FieldSpec nameField = FieldSpec.builder(String.class, "name")
.addAnnotation(Author.class)
.addModifiers(Modifier.PUBLIC) // 添加修饰符
.initializer("$S", "AAA") // 成员变量实例化
.build();
// 创建一个引用类型并初始化
FieldSpec stuField = FieldSpec.builder(Student.class, "student")
.addModifiers(Modifier.PRIVATE)
.initializer("new $T()", Student.class)
.build();
TypeSpec person = TypeSpec.classBuilder("Person") // 使用classBuilder创建类
.addField(idField)
.addField(nameField)
.addField(stuField)
.build();
JavaFile javaFile = JavaFile.builder("com.teaphy.poet.generated", person)
.build();
javaFile.writeTo(System.out);
生成的代码:
package com.teaphy.poet.generated;
import com.teaphy.poet.annotations.Author;
import com.teaphy.poet.module.Student;
import java.lang.String;
class Person {
int age;
@Author
public String name = "AAA";
private Student student = new Student();
}
在方法和构造函数上声明参数,使用ParameterSpec
,或MethodSpec.addParameter()
。对于ParameterSpec
而言,可以使用ParameterSpec.builder()
设置参数,也可以使用通过get
方法依据指定参数元素设置参数。
ParameterSpec.builder
的API:
addAnnotation
:设置注解addModifiers
:设置修饰类型例如:
ParameterSpec namePram = ParameterSpec.builder(String.class, "name")
.addAnnotation(Author.class)
.build();
ParameterSpec sexPram = ParameterSpec.builder(int.class, "gender")
.addModifiers(Modifier.FINAL)
.build();
// 构造函数
MethodSpec constructor = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(int.class, "id")
.addParameter(namePram)
.addParameter(sexPram)
.build();
TypeSpec person = TypeSpec.classBuilder("Person") // 使用classBuilder创建类
.addMethod(constructor)
.build();
生成的代码:
package com.teaphy.poet.generated;
import com.teaphy.poet.annotations.Author;
import java.lang.String;
class Person {
public Person(int id, @Author String name, final int gender) {
}
}
CodeBlock
是生成代码块的声明。代码块是.java文件的一个片段,可能包含声明,语句和文档
,代码块不一定是格式良好的Java代码,也不是经过验证的。
代码块支持格式化占位符。其中String.format(java.lang.String,java.lang.Object ...)
使用百分比%
来引用目标值,而在CodeBlock
中,使用美元符号$
并具有自己的一组允
许的占位符:
在JavaPoet中,CodeBlock是由静态方法join
、of
或者是CodeBlock.Builder的build()方法。
获取CodeBlock.Builder
可以通过CodeBlock
的静态方法builder()
或者toBuilder()
.
CodeBlock.Builder
的API
例如:
// 常规方法
Map map = new HashMap<>();
map.put("id", 1);
map.put("name", "tea");
CodeBlock codeBlock = CodeBlock.builder()
.add("$T a = $L", String.class, "A") // 设置代码,此方法并不添加分号和换行。
.add(";")
.add("\n")
.addNamed("\"id: $id:L, name: $name:L\"", map) // 设置代码块,通过命名参数方式格式化字符串,此方法并不添加分号和换行。
.add("\n")
.addStatement("int result = 1") // for
.beginControlFlow("for (int i = 0; i < 10; i++)")
.addStatement("result = result + i")
.endControlFlow()
.addStatement("long now = $T.currentTimeMillis()",
System.class) // if - else if - ... - else
.beginControlFlow("if ($T.currentTimeMillis() < now)", System.class)
.addStatement("$T.out.println($S)", System.class, "Time travelling, woo hoo!")
.nextControlFlow("else if ($T.currentTimeMillis() == now)", System.class)
.addStatement("$T.out.println($S)", System.class, "Time stood still!")
.nextControlFlow("else")
.addStatement("$T.out.println($S)", System.class, "Ok, time still moving forward")
.endControlFlow()
.addStatement("$T.out.println($S)", System.class, "Ok, time still moving forward")
.addStatement("$T i = $L", int.class, 0)
.addStatement("int id = 10") // while
.beginControlFlow("while( id > 0)")
.addStatement("$T.out.println(\"id: \" + $L)", System.class, "id")
.addStatement("id--")
.endControlFlow()
.beginControlFlow("do") // do while
.addStatement("i++")
.endControlFlow("while(i < 5)")
.beginControlFlow("try") // try - catch
.addStatement("throw new Exception($S)", "Failed")
.nextControlFlow("catch ($T e)", Exception.class)
.addStatement("throw new $T(e)", RuntimeException.class)
.endControlFlow()
.build();
生成的代码:
String a = A;
"id: 1, name: tea"
int result = 1;
for (int i = 0; i < 10; i++) {
result = result + i;
}
long now = System.currentTimeMillis();
if (System.currentTimeMillis() < now) {
System.out.println("Time travelling, woo hoo!");
} else if (System.currentTimeMillis() == now) {
System.out.println("Time stood still!");
} else {
System.out.println("Ok, time still moving forward");
}
System.out.println("Ok, time still moving forward");
int i = 0;
int id = 10;
while( id > 0) {
System.out.println("id: " + id);
id--;
}
do {
i++;
} while(i < 5);
try {
throw new Exception("Failed");
} catch (Exception e) {
throw new RuntimeException(e);
}
注意:
add()
&addNamed()
方法不添加分号和换行。若想分号或换行,需在字符串后缀添加\n
或者调用addStatement("id")
以添加分号或换行。$L
在JavaPoet中指的是没有转义的字面值,可以是字符串、基本数据类型、类型声明、注解甚至其他代码块。
例如:
FieldSpec priField = FieldSpec.builder(int.class, "pri")
.initializer("$L", 1) // 字面值为基本数据类型
.build();
FieldSpec strField = FieldSpec.builder(Class.class, "str")
.initializer("str", "tea") // 字面值为String
.build();
FieldSpec clzField = FieldSpec.builder(Class.class, "clz")
.initializer("$L", Student.class) // 字面值为类型声明
.build();
Annotation annoAuthor = Student.class.getAnnotation(Author.class);
FieldSpec annoField = FieldSpec.builder(Class.class, "anno")
.initializer("$L", annoAuthor) // 字面值为注解
.build();
CodeBlock codeBlock = CodeBlock.builder()
.beginControlFlow("if (a > b)")
.addStatement("return a")
.nextControlFlow("else")
.addStatement("return b")
.endControlFlow()
.build();
FieldSpec blockField = FieldSpec.builder(int.class, "block")
.initializer("$L", codeBlock) // 字面值为代码块
.build();
TypeSpec person = TypeSpec.classBuilder("Person") // 使用classBuilder创建类
.addField(priField)
.addField(strField)
.addField(clzField)
.addField(annoField)
.addField(blockField)
.build();
JavaFile javaFile = JavaFile.builder("com.teaphy.poet.generated", person)
.build();
javaFile.writeTo(System.out);
生成的代码
class Person {
int pri = 1;
Class str = str;
Class clz = class com.teaphy.poet.module.Student;
Class anno = @com.teaphy.poet.annotations.Author(name=teaphy);
int block = if (a > b) {
return a;
} else {
return b;
}
;
}
$S
在JavaPoet中像String.format
中%s
一样,字符串的模板,将指定的字符串替换到$S
的
地方。
例如:
// 创建一个成员变量并初始化 (name = "AAA")
FieldSpec nameField = FieldSpec.builder(String.class, "name")
.initializer("$S", "AAA") // 成员变量实例化
.build();
TypeSpec person = TypeSpec.classBuilder("Person") // 使用classBuilder创建类
.addField(nameField)
.build();
JavaFile javaFile = JavaFile.builder("com.teaphy.poet.generated", person)
.build();
javaFile.writeTo(System.out);
生成的代码:
class Person {
String name = "AAA";
}
生成的代码通常是自引用的。$N
在JavaPoet中代指的是一个名称,使用$N
通过其名称引用另一个生成的声明,例如调用的方法名称,变量名称,这一类存在意思的名称。
例如:
FieldSpec aField = FieldSpec.builder(int.class, "a").build();
FieldSpec bField = FieldSpec.builder(int.class, "b").build();
MethodSpec sumSpec = MethodSpec.methodBuilder("sum")
.addModifiers(Modifier.PRIVATE)
.returns(int.class)
.addStatement("return $N + $N", aField, bField) // 引用变量名称
.build();
MethodSpec getSum = MethodSpec.methodBuilder("getSum")
.addModifiers(Modifier.PUBLIC)
.returns(int.class)
.addStatement("return $N()", sumSpec) // 引用方法名称
.build();
TypeSpec person = TypeSpec.classBuilder("Person") // 使用classBuilder创建类
.addField(aField)
.addField(bField)
.addMethod(sumSpec)
.addMethod(getSum)
.build();
JavaFile javaFile = JavaFile.builder("com.teaphy.poet.generated", person)
.build();
javaFile.writeTo(System.out);
生成的代码:
class Person {
int a;
int b;
private int sum() {
return a + b;
}
public int getSum() {
return sum();
}
}
$T
在JavaPoet代指的是TypeName,该模板主要将Class抽象出来,用传入的TypeName指向的Class来代替;也可以是TypeMirror,或者是Element。另外,在JavaPoet中,$T
不仅支持内置类型,还包括自动生成import
语句。
例如:
ClassName className = ClassName.get("com.teaphy.poet.generated", "Student");
MethodSpec stuMethod = MethodSpec.methodBuilder("getStudent")
.returns(className)
.addStatement("return $T()", className)
.build();
MethodSpec dateMethod = MethodSpec.methodBuilder("getDate")
.returns(Date.class)
.addStatement("return $T()", Date.class)
.build();
TypeSpec person = TypeSpec.classBuilder("Person") // 使用classBuilder创建类
.addMethod(stuMethod)
.addMethod(dateMethod)
.build();
JavaFile javaFile = JavaFile.builder("com.teaphy.poet.generated", person)
.build();
javaFile.writeTo(System.out);
生成的代码:
package com.teaphy.poet.generated;
import java.util.Date;
import com.teaphy.poet.module.Student;
class Person {
Student getStudent() {
return Student();
}
Date getDate() {
return Date();
}
}
$T
在JavaPoet中表示美元符号$
。
$W
在JavaPoet中表示空格或换行符,具体取决于它在代码行的位置。
$Z
$>
在JavaPoet中表示增加缩进级别。
$<
在JavaPoet中表示减少缩进级别。
$[
在JavaPoet中表示开始发表声明。对于多行语句,第一行之后的每一行都是双缩进的。
$]
在JavaPoet中表示结束声明。
CodeBlock
可以通过几种方式指定其占位符的值。CodeBlock
上的每个操作只能
使用一种样式。
将格式字符串中每个占位符的参数值传递给CodeBlock.add()
。
例如:
CodeBlock relative = CodeBlock.builder()
.add("I eat $L $L", 3, "apple")
.addStatement("") // 添加换行
.build();
生成的代码:
I eat 3 apple;
在格式字符串中的占位符之前放置一个整数索引(从1开始),以指定要使用的参数
。
例如:
CodeBlock position = CodeBlock.builder()
.add("I eat $2L $1L", "apple", 3)
.addStatement("")
.build();
生成的代码:
I eat 3 apple;
使用语法$argumentName:X
,其中X是格式字符,并使用包含格式字符串中所有
参数键的Map传递给CodeBlock.addNamed()
或者MethodSpec.Builder.addNamedC ode()
。注意:参数名称使用a-z
,A-Z
,0-9
和_
中的字符,并且必须以小写
字符开头。
例如:
Map map = new HashMap<>();
map.put("count", 3);
map.put("name", "apple");
CodeBlock named = CodeBlock.builder()
.addNamed("I eat $count:L $name:L", map)
.addStatement("")
.build();
生成的代码:
I eat 3 apple;
在JavaPoet中,TypeName
只是简单的标识符,并不对它们所命名的值建模,其包括声明类型(ClassName
)、数组(ArrayTypeName
)、参数化类型(ParameterizedTypeName
)、通配符类型(WildcardTypeName
)和类型变量(TypeVariableName
)。
例如:
// 声明类型
ClassName stu = ClassName.get("com.teaphy.poet.module", "Student");
MethodSpec stuMethod = MethodSpec.methodBuilder("getStudent")
.returns(stu)
.addStatement("return $T()", stu)
.build();
ClassName list = ClassName.get("java.util", "List");
ClassName arrayList = ClassName.get("java.util", "ArrayList");
// 参数化类型
TypeName listOfStudent = ParameterizedTypeName.get(list, stu);
MethodSpec allMethod = MethodSpec.methodBuilder("getAll")
.returns(listOfStudent)
.addStatement("$T result = new $T()", listOfStudent, arrayList)
.addStatement("result.add(new $T())", stu)
.addStatement("result.add(new $T())", stu)
.addStatement("result.add(new $T())", stu)
.addStatement("return result")
.build();
// 数组
// 1. of - 通过指定元素类型生成包含该元素的数组
// 2. get - 获取mirror/type等效的数组类型的标识符
TypeName stus = ArrayTypeName.of(Student.class);
FieldSpec stusField = FieldSpec.builder(stus, "stus")
.initializer("new $T()", stus)
.build();
// 类型变量
TypeVariableName tTypeVariable = TypeVariableName.get("T");
FieldSpec tField = FieldSpec.builder(tTypeVariable, "t").build();
MethodSpec tMethod = MethodSpec.methodBuilder("tyepVar")
.addTypeVariable(tTypeVariable)
.returns(tTypeVariable)
.addStatement("return $N", tField)
.build();
TypeSpec tvSpec = TypeSpec.classBuilder("Sex")
.addTypeVariable(tTypeVariable)
.addField(tField)
.addMethod(tMethod)
.build();
// 通配符类型
Field stuListField = ClassName_Demo.class.getDeclaredField("stuList");
Type stuLisGenericType = stuListField.getGenericType();
ParameterizedType parameterizedType = (ParameterizedType) stuLisGenericType;
Type[] typesNum = parameterizedType.getActualTypeArguments();
WildcardType wildcardType = (WildcardType) typesNum[0];
TypeName wildcardTypeName = WildcardTypeName.get(wildcardType);
// 参数化类型
TypeName wildcardTypeNameList = ParameterizedTypeName.get(list, wildcardTypeName);
FieldSpec clzField = FieldSpec.builder(wildcardTypeNameList, "clzStudent")
.build();
TypeSpec person = TypeSpec.classBuilder("Person") // 使用classBuilder创建类
.addField(clzField)
.addField(stusField)
.addMethod(stuMethod)
.addMethod(allMethod)
.addType(tvSpec)
.build();
JavaFile javaFile = JavaFile.builder("com.teaphy.poet.generated", person)
.build();
javaFile.writeTo(System.out);
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
生成的代码:
package com.teaphy.poet.generated;
import com.teaphy.poet.module.Student;
import java.util.ArrayList;
import java.util.List;
class Person {
List extends Student> clzStudent;
Student[] stus = new Student[]();
Student getStudent() {
return Student();
}
List getAll() {
List result = new ArrayList();
result.add(new Student());
result.add(new Student());
result.add(new Student());
return result;
}
class Sex {
T t;
T tyepVar() {
return t;
}
}
}
基本数据类型和void是可以直接引用的常量。
JavaFile是一个包含top_level
类的Java文件,在其内声明了一系格式化输出/写入(writeTo
)操作。可以通过JavaFile.Builder
设置JavaFile
的属性,而·JavaFile.Builder
的实例需要指定包名和声明类型(类、接口、枚举、注解等等)并调用JavaFile.builder()
静态方法来创建。
writeTo
Java
文件格式化输出Java
文件写入指定的FileJava
文件写入注解处理器创建的.java
文件Java
文件写入指定路径的FileJavaFile.Builder的API:
addFileComment
:设置注释
addStaticImport
:设置静态导入
indent
:设置在Java文件中表示缩进的字符串,默认是空格
skipJavaLangImports
:设置是否省略java.lang
中类的导入
例如:
// 使用classBuilder创建类
TypeSpec person = TypeSpec.classBuilder("Person")
.addField(FieldSpec.builder(int.class, "id").build())
.addField(FieldSpec.builder(String.class, "name").build())
.build();
JavaFile javaFile = JavaFile.builder("com.teaphy.poet.generated", person)
.addFileComment("这是Person类的注释") // 添加注释
.addStaticImport(CommonConst.class, "VERSION", "VERSION_CODE") // 通过指定类和静态方法/常量名静态导入方法/常量
.addStaticImport(Gendar.MAN) // 静态导入枚举值
.indent("--->") // 设置表示缩进的字符串
.skipJavaLangImports(true) // 设置是否省略`java.lang`中类的导入。此时省略了`java.lang.String`的导入
.build();
javaFile.writeTo(System.out);
生成的代码:
// 这是Person类的注释
package com.teaphy.poet.generated;
import static com.teaphy.poet.globle.CommonConst.VERSION;
import static com.teaphy.poet.globle.CommonConst.VERSION_CODE;
import static com.teaphy.poet.globle.Gendar.MAN;
class Person {
--->int id;
--->String name;
}