JavaPoet
javapoet,产生 Java 源文件
https://github.com/square/javapoet
JavaPoet API 说明
FieldSpec 创建字段(属性)
代表一个成员变量,一个字段声明
- builder(Type type, String name, Modifier… modifiers) 字段类型,字段名,字段修饰符
- addModifiers(Modifier… modifiers) 字段修饰符
- initializer(String format, Object… args) 初始化参数
1
2
3
4
| FieldSpec fieldSpec = FieldSpec.builder(String.class, "android") // 等价于 String android;
.addModifiers(Modifier.PRIVATE, Modifier.FINAL) // 等价于 private final String android;
.initializer("$S", "Android") // 等价于 private final String android = "Android";
.build();
|
Modifier
Modifier 包括我们用到的所有修饰符:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| public enum Modifier {
PUBLIC,
PROTECTED,
PRIVATE,
ABSTRACT,
DEFAULT, // JDK1.8
STATIC,
FINAL,
TRANSIENT,
VOLATILE,
SYNCHRONIZED,
NATIVE,
STRICTFP;
}
|
MethodSpec 创建方法
代表一个方法或者构造方法声明
- constructorBuilder() 构造方法
- methodBuilder(String name) 方法名
- addModifiers(Modifier… modifiers) 方法修饰符
- addParameter(Type type, String name, Modifier… modifiers) 方法参数列表
- addStatement(String format, Object… args) 方法代码段,会自动导包
- addCode(String format, Object… args) 方法代码段;一股脑的添加所有代码。但是我们需要自己换行,输入分号和缩进不会导包
- returns(Type returnType) 返回值
- beginControlFlow(String controlFlow, Object… args) 流程控制开始
- endControlFlow() 流程控制结束
- addJavadoc(String format, Object… args) 添加方法文档注释
addAnnotation(Class<?> annotation)
方法上面添加注解
构造方法
1
2
3
4
5
6
7
8
9
10
11
12
13
| MethodSpec methodSpec = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.build();
TypeSpec typeSpec = TypeSpec.classBuilder("Test")
.addMethod(methodSpec)
.build();
/*
class Test {
public Test() {
}
}*/
|
案例
案例 1:
1
2
3
4
5
6
| MethodSpec methodSpec = MethodSpec.methodBuilder("test")
.returns(void.class) // void test() {}
.addModifiers(Modifier.PUBLIC) // public void test() {}
.addParameter(String.class, "str") // public void test(String str) {}
.addStatement("System.out.println(str)")
.build();
|
输出:
1
2
3
| public void test(String str) {
System.out.println(str);
}
|
案例 2:
1
2
3
4
5
6
7
| MethodSpec methodSpec = MethodSpec.methodBuilder("test")
.addCode(""
+ "int total = 0;\n"
+ "for (int i = 0; i < 10; i++) {\n"
+ " total += i;\n"
+ "}\n")
.build();
|
输出:
1
2
3
4
5
6
| void test() {
int total = 0;
for (int i = 0; i < 10; i++) {
total += i;
}
}
|
TypeSpec 创建类、接口或枚举
代表一个类,接口,或者枚举声明
创建类
- classBuilder(String name) 类,接口或枚举名
- addModifiers(Modifier… modifiers) 类修饰符
- addMethod(MethodSpec methodSpec) 类中添加方法
- addField(Type type, String name, Modifier… modifiers) 类中添加字段
1
2
| // class Test {}
TypeSpec.classBuilder("Test").build();
|
创建枚举
- enumBuilder(String name) 生成一个 name 枚举
1
2
3
4
5
| /*
enum Test {
ONE
}*/
TypeSpec typeSpec = TypeSpec.enumBuilder("Test").addEnumConstant("ONE").build();
|
创建接口
- interfaceBuilder(String name) 生成一个 name 接口
1
2
| // interface Test {}
TypeSpec.interfaceBuilder("Test").build();
|
JavaFile 输出文件
JavaFile 用于输出包含单个顶级类的 Java 文件。
- builder(String packageName, TypeSpec typeSpec) 包名,类名
addStaticImport(Class<?> clazz, String… names)
静态导包- writeTo(Appendable out) 写入到 xxx
案例 1:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
| private static void test1() {
FieldSpec fieldSpec = FieldSpec.builder(String.class, "android") // 等价于 String android;
.addModifiers(Modifier.PRIVATE, Modifier.FINAL) // 等价于 private final String android;
.initializer("$S", "Android") // 等价于 private final String android = "Android";
.build();
MethodSpec methodSpec = MethodSpec.methodBuilder("test")
.returns(void.class) // void test() {}
.addModifiers(Modifier.PUBLIC) // public void test() {}
.addParameter(String.class, "str") // public void test(String str) {}
.addStatement("System.out.println(str)")
.build();
// public void test(String str) {
// System.out.println(str);
// }
TypeSpec typeSpec = TypeSpec.classBuilder("Test")
.addMethod(methodSpec)
.addField(fieldSpec)
.build();
JavaFile javaFile = JavaFile.builder("me.hacket.apt.demo", typeSpec)
.build();
try {
javaFile.writeTo(System.out);
} catch (IOException e) {
e.printStackTrace();
}
}
|
输出:
1
2
3
4
5
6
7
8
9
10
11
| package me.hacket.apt.demo;
import java.lang.String;
class Test {
private final String android = "Android";
public void test(String str) {
System.out.println(str);
}
}
|
进阶用法
ControlFlow
使用 addStatement
可以帮我们添加分号和换行,而使用 beginControlFlow
和 endControlFlow
组合可以帮我们轻松实现控制流代码:
for 循环
1
2
3
4
5
6
7
8
9
10
11
12
| MethodSpec methodSpec = MethodSpec.methodBuilder("test")
.addStatement("int total = 0")
.beginControlFlow("for (int i = 0; i < 10; i++)")
.addStatement("total += i")
.endControlFlow()
.build();
TypeSpec typeSpec = TypeSpec.classBuilder("Test")
.addMethod(methodSpec)
.build();
JavaFile javaFile = JavaFile.builder("me.hacket.apt.demo", typeSpec)
.build();
javaFile.writeTo(System.out);
|
输出:
1
2
3
4
5
6
7
8
| class Test {
void test() {
int total = 0;
for (int i = 0; i < 10; i++) {
total += i;
}
}
}
|
其他控制语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // do... while
.beginControlFlow("do")
.endControlFlow("while (true)")
// if... else if... else...
.beginControlFlow("if (true)")
.nextControlFlow("else if (false)")
.nextControlFlow("else")
.endControlFlow()
// try... catch... finally
.beginControlFlow("try")
.nextControlFlow("catch ($T e)", Exception.class)
.addStatement("e.printStackTrace()")
.nextControlFlow("finally")
.endControlFlow()
|
占位符
$S (String)字符串
当代码中包含字符串的时候, 可以使用 $S
表示
1
2
3
4
5
6
| private static MethodSpec whatsMyName(String name) {
return MethodSpec.methodBuilder(name)
.returns(String.class)
.addStatement("return $S", name)
.build();
}
|
$L (Literal)字面量
$L
是字面量替换,它与 $S
相似,但是它并不需要转义,也就是不包含字符串的引号。
1
2
3
4
5
6
7
8
9
10
| private MethodSpec computeRange(String name, int from, int to, String op) {
return MethodSpec.methodBuilder(name)
.returns(int.class)
.addStatement("int result = 0")
.beginControlFlow("for (int i = $L; i < $L; i++)", from, to)
.addStatement("result = result $L i", op)
.endControlFlow()
.addStatement("return result")
.build();
}
|
$T (Type)非基础类型的类
上面例子为了简单,都使用的是一些基础类型,为的是不需要导包。实际中我们需要使用大量对象,如果只是在字符串中写死,代码虽没有问题,但是没有导包还是会保错。这是可以考虑使用 $T,它的作用是替换类型。
1
2
3
4
| MethodSpec today = MethodSpec.methodBuilder("today")
.returns(Date.class)
.addStatement("return new $T()", Date.class)
.build();
|
$N ( Name)
$N是名称替换。例如我们定义了一个getXXX的方法,我们调用它时可以使用addStatement("get$
L()”, “XXX”)
这种写法实现,但是每次拼接 “get” 未免太麻烦了,一个不留心说不定还忘记了。那么使用 addStatement(“$N()”, methodSpec) 就更加方便了。
1
2
3
4
5
6
7
8
9
| MethodSpec methodSpec = MethodSpec.methodBuilder("get" + name)
.returns(String.class)
.addStatement("return $S", name)
.build();
MethodSpec.methodBuilder("getValue")
.returns(String.class)
.addStatement("return $N()", methodSpec)
.build();
|
继承与接口
1
2
3
4
5
6
| TypeSpec.classBuilder("Test")
.superclass(String.class)
.addSuperinterface(Serializable.class)
.build();
// class Test extends String implements Serializable {}
|
泛型
1
2
3
4
5
6
7
8
| FieldSpec.builder(TypeVariableName.get("T"), "mT", Modifier.PRIVATE).build();
// private T mT;
TypeVariableName mTypeVariable = TypeVariableName.get("T");
ParameterizedTypeName mListTypeName = ParameterizedTypeName.get(ClassName.get(List.class), mTypeVariable);
FieldSpec fieldSpec = FieldSpec.builder(mListTypeName, "mList", Modifier.PRIVATE).build();
// private List<T> mList;
|
方法和类中使用 addTypeVariable
添加泛型。
初始化块
1
2
3
4
5
6
7
8
9
10
11
12
13
| TypeSpec.classBuilder("Test")
.addStaticBlock(CodeBlock.builder().build())
.addInitializerBlock(CodeBlock.builder().build())
.build();
/*
class Test {
static {
}
{
}
}*/
|
Annotations
添加注解的方法可以直接使用 addAnnotation()
。
1
2
3
4
5
6
| MethodSpec toString = MethodSpec.methodBuilder("toString")
.addAnnotation(Override.class)
.returns(String.class)
.addModifiers(Modifier.PUBLIC)
.addStatement("return $S", "Hoverboard")
.build();
|
如果需要给注解设置属性,那么需要使用 AnnotationSpec
:
1
2
3
4
5
6
7
8
9
| AnnotationSpec.builder(Headers.class)
.addMember("accept", "$S", "application/json; charset=utf-8")
.addMember("userAgent", "$S", "Square Cash")
.build()
/*@Headers(
accept = "application/json; charset=utf-8",
userAgent = "Square Cash"
)*/
|
匿名内部类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| TypeSpec.anonymousClassBuilder("") // <- 也可添加参数
.superclass(Runnable.class)
.addMethod(MethodSpec.methodBuilder("run")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.returns(TypeName.VOID)
.build())
.build();
/*
new Runnable() {
@Override
public void run() {
}
}*/
|
Javadoc
添加注释可以使用 addJavadoc
,直接传入注释字符串就行了
简单用法
案例 1:生成一个 HelloWorld
1
2
3
4
5
6
7
8
9
10
11
12
13
| MethodSpec mainMethodSpec = MethodSpec.methodBuilder("main") // 方法名
.addModifiers(Modifier.PUBLIC, Modifier.STATIC) // 方法修饰符
.addParameter(String[].class, "args") // 添加string[]类型的名为args的参数
.addStatement("$T.out.println($S)", System.class, "Hello World!") // 添加代码,这里$T和$S后面会讲,这里其实就是添加了System,out.println("Hello World");
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld") // 类名
.addModifiers(Modifier.PUBLIC, Modifier.STATIC) // 类修饰符
.addMethod(mainMethodSpec) // 在类中添加方法
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld).build();
javaFile.writeTo(System.out);
|
生成的文件:
1
2
3
4
5
6
7
8
9
10
| package com.example.helloworld;
import java.lang.String;
import java.lang.System;
public static class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}
|
案例 2:for 循环
1
2
3
4
5
6
7
8
9
| MethodSpec mainMethodSpec = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(String[].class, "args")
.addCode(""
+ "int total = 0;\n"
+ "for (int i = 0; i < 10; i++) {\n"
+ " total += i;\n"
+ "}\n") // for循环
.build();
|
1
2
3
4
5
6
7
8
| mainMethodSpec = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(String[].class, "args")
.addStatement("int total = 0")
.beginControlFlow("for (int i = 0; i < 10; i++)") // 控制流语句
.addStatement("total += i")
.endControlFlow()
.build();
|
- addCode() 和 addStatement() 区别
addStatement() 生成的代码,自动缩进的,自动加分号、换行的和自动导包的。
addCode() 什么都没有
占位符
- $L 字面量
- $S for Strings
- $T for Types(在生成的源码会自动导入你的类包)
- $N for Names(我们自己生成的方法名或者变量名等)
添加变量 FieldSpec
1
2
3
4
5
6
7
8
9
10
11
12
13
| FieldSpec android = FieldSpec.builder(String.class, "android")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.initializer("$S + $L", "Lollipop v.", 5.0d)
.build();
TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC)
.addField(android)
.addField(String.class, "robot", Modifier.PRIVATE, Modifier.FINAL)
.build();
JavaFile javaFile = JavaFile.builder("me.hacket.demo", typeSpec).build();
javaFile.writeTo(System.out);
|
生成的代码:
1
2
3
4
5
6
7
8
9
| package me.hacket.demo;
import java.lang.String;
public class HelloWorld {
private final String android = "Lollipop v." + 5.0;
private final String robot;
}
|
生成构造方法
1
2
3
4
5
6
7
| MethodSpec methodSpec = MethodSpec.constructorBuilder().build();
TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC)
.addMethod(methodSpec)
.build();
JavaFile javaFile = JavaFile.builder("me.hacket.demo", typeSpec).build();
javaFile.writeTo(System.out);
|
生成的代码:
1
2
3
4
5
6
| package me.hacket.demo;
public class HelloWorld {
HelloWorld() {
}
}
|
http://www.jianshu.com/p/95f12f72f69a
Ref
https://juejin.cn/post/6844903475776585741
kotlinpoet
文档:https://square.github.io/kotlinpoet/