JavaPoet的使用和示例
JavaPoet使用面向对象的方式来构建类,其中主要分为三个部分,类,属性和方法。一般构建顺序为先属性,再方法,最后创建类的时候把前面创建的属性和方法都添加到类中即可。
导入依赖
1
implementation 'com.squareup:javapoet:1.13.0'
关键API
Type && TypeName
Type
Type表示类的基础class类型,例如常见的基础类型int.class
,又或者对象类型String.class
、File.class
等,以及数组类型File[].class
,使用这个不用手动导包,便捷方便。
TypeName
当有些库不在标准库中,而我们又不想在这个模块中引入过多的别的三方库时,我们可以使用TypeName
来实现导入对应库的能力。当然可以使用Type
的地方都是可以使用TypeName
的。
-
基础变量类型可以使用
TypeName.BOOLEAN
-
数组类型可以使用
ArrayTypeName.of(boolean.class)
又或者ArrayTypeName.of(TypeName.BOOLEAN)
-
不能直接使用到的类可以使用
ClassName.get(“java.io”, “File”)
来获取TypeName
并导入对应的包。当需要使用TypeName
的地方也可以使用ClassName.get(File.class)
来获取对呀的TypeName
类型。 -
属性支持范型,需要使用
ParameterizedTypeName.get()
,使用ParameterizedTypeName.get(List.class, Integer.class)
可以得到List<Integer>
,因为范型是不支持基础变量类型的,所以不能使用int.class
或TypeName.INT
。当然也可以使用TypeName
来构建,ParameterizedTypeName.get(ClassName.get("java.util", "Map"), TypeName.get(Integer.class), TypeName.get(String.class)
,这个构建比较曲折,因为在此处Type
类型的并不能与TypeName
进行混用。 -
范型属性,
TypeVariableName.get("T")
-
通配符类型,这个需要用在类或者支持范型的属性中,
WildcardTypeName.subtypeOf(String.class)
,输出? extends String
属性
构建属性使用FieldSpec
,通过builder(type,name,...modifiers)
,可以指定属性的类型、名称以及修饰符。通过addModifiers()
同样可以设置对应的修饰符。通过使用initializer()
可以给属性赋初始值。通过addAnnotation()
可以增加注解,具体看如下例子
基础变量属性
期望private volatile int age = 18;
1
2
3
4
5
FieldSpec age = FieldSpec.builder(int.class, "age")
.initializer("18")
.addModifiers(Modifier.VOLATILE)
.addModifiers(Modifier.PRIVATE)
.build();
通过手动设置对象路径的方式
期望public static Object lock = new Object;
1
2
3
4
5
6
FieldSpec lock = FieldSpec.builder(
ClassName.get("java.lang", "Object"),
"lock",
Modifier.PUBLIC, Modifier.STATIC
).initializer("new Object()")
.build();
使用Map
期望public Map<String, Integer> cacheMap;
1
2
3
4
5
6
FieldSpec name = FieldSpec.builder(
ParameterizedTypeName.get(Map.class, String.class, Integer.class),
"cacheMap"
)
.addModifiers(Modifier.PUBLIC)
.build();
使用范型
期望private T data;
,需要类也设置范型。
1
2
3
FieldSpec data = FieldSpec.builder(TypeVariableName.get("T"), "data")
.addModifiers(Modifier.PRIVATE)
.build();
使用通配符
期望private List<? extends String> list;
1
2
3
4
5
6
7
8
FieldSpec list = FieldSpec.builder(
ParameterizedTypeName.get(
ClassName.get(List.class),
WildcardTypeName.subtypeOf(String.class)
), "list"
)
.addModifiers(Modifier.PRIVATE)
.build();
方法
方法使用MethodSpec
来创建。方法修饰符、注解和属性基本一致,使用addTypeVariable()
来增加方法范型,returns()
来设置方法返回类型,通过addParameter()
增加方法入参,使用addException()
来设置方法可以抛出的异常。比较困难的就是方法体里面的内容了,方法体里的内容可以使用CodeBlock
来表示,最后通过addCode()
方式添加。
CodeBlock
CodeBlock
主要用来构建方法内容,主要添加代码的方式为addStatement()
来逐行添加。同样的,MethodSpec
也是可以使用这个方法的,CodeBlock
主要提供一个方便的解耦方式。addStatement()
提供三种代码替换的方式:
-
addStatement("$T file", File.class)
=>File file;
,$T
表示会主动把对应的包import
进来。 -
addStatement("$L = null", "file")
=>file = null;
,$L
只是简单的替换。 -
addStatement("$S.length()", "foo")
=>"foo".length();
,$S
表示字符串替换,会带有双引号。
常规的addStatement()
都会在语句的末位自动带上“;“
作为结束,而在一些语句中并不需要如此,例如if
语句等,这是需要用到如下语句来制定作用域。括号之间需要满足Java的支持语法规范。
-
beginControlFlow()
会在代码结束为止补上"{"
。 -
nextControlFlow()
会在代码开始钱补上"}"
,在开始位置补上"{"
. -
endControlFlow()
则会在开始位置补上"}"
。
对于不需要“;“
、"{"
和“}"
,可以直接使用addCode(String)
的方式来添加,例如switch语句
代码段示例
if语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* if(i > 10) {
* System.out.println("true");
* } else if(i < 10) {
* System.out.println("false");
* } else {
* System.out.println("undefine");
* }
*/
CodeBlock codeBlock = CodeBlock.builder()
.beginControlFlow("if(i > 10)")
.addStatement("System.out.println($S)", "true")
.nextControlFlow("else if(i < 10)")
.addStatement("System.out.println($S)", "false")
.nextControlFlow("else")
.addStatement("System.out.println($S)", "undefine")
.endControlFlow()
.build();
for循环
1
2
3
4
5
6
7
8
9
10
/**
* for (int i = 0; i < 5; i++) {
* System.out.print("str1 value" + i);
* }
*/
CodeBlock codeBlock = CodeBlock.builder()
.beginControlFlow("for (int i = 0; i < 5; i++)")
.addStatement("System.out.print($S + i)", "str1 value")
.endControlFlow()
.build();
while语句
1
2
3
4
5
6
7
8
9
10
11
12
/**
* while(i > 10) {
* System.out.println("currentCount: " + i);
* i--;
* }
*/
CodeBlock codeBlock = CodeBlock.builder()
.beginControlFlow("while(i > 10)")
.addStatement("System.out.println($S + i)", "currentCount: ")
.addStatement("i--")
.endControlFlow()
.build();
do while语句
1
2
3
4
5
6
/**
* do {
* } while (i > 10);
*/
do {
} while (i > 10);
try catch语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* try {
* } catch (Exception e) {
* e.printStackTrace();
* } finally {
* }
*/
CodeBlock codeBlock = CodeBlock.builder()
.beginControlFlow("try")
.nextControlFlow("catch ($T e)", Exception.class)
.addStatement("e.printStackTrace()")
.nextControlFlow("finally")
.endControlFlow()
.build();
类
类使用TypeSpec
来创建。修饰符、范型都与方法是一样的。另外还可以使用addMethod()
来添加方法,addField()
来增加属性。
正常类
1
2
3
4
5
6
7
/*
* public class Main {
* }
*/
TypeSpec typeSpec = TypeSpec.classBuilder("Main")
.addModifiers(Modifier.PUBLIC)
.build();
枚举类
1
2
3
4
5
6
7
8
/*
* enum Main {
* DEMO
* }
*/
TypeSpec typeSpec = TypeSpec.enumBuilder("Main")
.addEnumConstant("DEMO")
.build();
注解类
1
2
3
4
5
6
/*
* @interface Main {
* }
*/
TypeSpec typeSpec = TypeSpec.annotationBuilder("Main")
.build();
接口
1
2
3
4
5
6
/*
* interface Main {
* }
*/
TypeSpec typeSpec = TypeSpec.interfaceBuilder("Main")
.build();
匿名内部类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// void test() {
// new Thread(new Runnable() {
// @Override
// public void run() {
// }
// });;
// }
TypeSpec typeSpec = TypeSpec.anonymousClassBuilder("")
.superclass(Runnable.class)
.addMethod(
MethodSpec.methodBuilder("run")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.build()
)
.build();
MethodSpec methodSpec = MethodSpec
.methodBuilder("test")
.addStatement("new $T($L);", Thread.class, typeSpec)
.build();