【转载】JavaPoet 看这一篇就够了

Posted by Owen Lee on 2021-04-09

重要:
本文转载自:https://juejin.cn/post/6844903475776585741

Android/Java 框架常常会使用编译期注解, 而其中关键的一步是如何生成 Java 文件. JavaPoet 就是用来生成 Java 代码的一个 Java Library.

基础概念

首先, 让我们来看一个简单的 Java 文件:

image

可以见, 一个 Java 文件由四部分组成。

也就是我们要生成一个 java 文件, 就是要生成上述四部分内容。

对应到 JavaPoet 中, 我们来看一张对比图:

image

可见, JavaFile 对应的 Java 文件。 包名直接用一个字符串表示。 TypeSpec 表示一个 class 定义。 FieldSpec 表示类中的属性。 MethodSpec 表示类中的方法。

最简实例

理解了 JavaPoet 的大致结构, 我们就可以生成一个简单的 Java 文件了。

gradle 引用:

1
compile 'com.squareup:javapoet:1.8.0'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class HowToJavaPoetDemo {
public static void main(String[] args) {
// `JavaFile` 代表 Java 文件
JavaFile javaFile = JavaFile.builder("com.walfud.howtojavapoet",
// TypeSpec 代表一个类
TypeSpec.classBuilder("Clazz")
// 给类添加一个属性
.addField(FieldSpec.builder(int.class, "mField", Modifier.PRIVATE)
.build())
// 给类添加一个方法
.addMethod(MethodSpec.methodBuilder("method")
.addModifiers(Modifier.PUBLIC)
.returns(void.class)
.addStatement("System.out.println(str)")
.build())
.build())
.build();

System.out.println(javaFile.toString());
}
}

更多的数据类型

上个例子中, 我们为了单一目标, 特意简化了数据类型。 现实编程中绝大多数情况下要面对各种对象类型, 如 File / List / Activity(android.app.Activity) 等等。 下面我们来了解一下 JavaPoet 的类型系统。

我们要定义一个 java.io.File 类型的变量, 比如这样:

1
2
3
4
5
import java.io.File;

public class ... {
private File mFile;
}

我们需要使用 FieldSpec.builder(File.class, “mFile”, Modifier.PRIVATE).build() 来定义这个属性。 这样 JavaPoet 就可以自动找到 File 所在的包并帮我们自动引入 import java.io.File

但如果定义 List 这种泛型的话, 该怎么写呢?List.class 显然是不行的。 因为这是一个复合类型, 其中的参数类型无法被表示。

因此, JavaPoet 帮我们定义了如下几种专门描述类型的类。其关系图如下:
image

在 Statement 中使用类型

上面介绍了 JavaPoet 的类型系统, 现在我们来看看最后一道坎: statemenet 中的类型占位符。

1
2
3
4
5
MethodSpec.methodBuilder("method")
.addStatement("$T file", File.class) // File file;
.addStatement("$L = null", "file") // file = null;
.addStatement("file = new File($S)", "foo/bar") // file = new File("foo/bar");
.build();
  • $ T 是类型替换, 一般用于 (“$T foo”, List.class) => List foo. $ T 的好处在于 JavaPoet 会自动帮你补全文件开头的 import. 如果直接写 (“List foo”) 虽然也能生成 List foo, 但是最终的 java 文件就不会自动帮你添加 import java.util.List.
  • $ L 是字面量替换, 比如 (“abc$L123”, “FOO”) => abcFOO123. 也就是直接替换.
  • $ S 是字符串替换, 比如: (“$S.length()”, “foo”) => “foo”.length() 注意 $ S 是将参数替换为了一个带双引号的字符串. 免去了手写 ““foo”.length()” 中转义 (\ “) 的麻烦.
  • $ N 是名称替换, 比如你之前定义了一个函数 MethodSpec methodSpec = MethodSpec.methodBuilder(“foo”).build(); 现在你可以通过 $ N 获取这个函数的名称 (“$N”, methodSpec) => foo.

万能例子(重点!!!要考!!!)

最后, 我们展示一段几乎涵盖你所常见 case 的例子 (仅为了展示 JavaPoet 用法, 生成的代码可能编译不过). 如果哪里不知道怎么生成的, 可以方便的在下面的生成代码里查找生成方法.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.walfud.howtojavapoet;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.lang.Comparable;
import java.lang.Exception;
import java.lang.Integer;
import java.lang.Object;
import java.lang.Override;
import java.lang.Runnable;
import java.lang.RuntimeException;
import java.lang.String;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class Clazz<T> extends String implements Serializable, Comparable<String>, Map<T, ? extends String> {
static {
}

private int mInt;

private int[] mArr;

private File mRef;

private T mT;

private List<String> mParameterizedField;

private List<? extends String> mWildcardField;

{
}

public Clazz() {
}

@Override
public <T> int method(String string, T t, Map<Integer, ? extends T> map) throws IOException,
RuntimeException {
int foo = 1;
String bar = "a string";
Object obj = new HashMap<Integer, ? extends T>(5);
baz(new Runnable(String param) {
@Override
void run() {
}
});
for (int i = 0; i < 5; i++) {
}
while (false) {
}
do {
} while (false);
if (false) {
} else if (false) {
} else {
}
try {
} catch (Exception e) {
e.printStackTrace();
} finally {
}
return 0;
}

class InnerClass {
}
}

JavaPoet 代码:

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package com.walfud.howtojavapoet;


import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;
import com.squareup.javapoet.TypeVariableName;
import com.squareup.javapoet.WildcardTypeName;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.lang.model.element.Modifier;

/**
* http://android.walfud.com/javapoet-看这一篇就够了/
*/
class HowToJavaPoetDemo {
public static void main(String[] args) {
TypeSpec clazz = clazz(builtinTypeField(), // int
arrayTypeField(), // int[]
refTypeField(), // File
typeField(), // T
parameterizedTypeField(), // List<String>
wildcardTypeField(), // List<? extends String>
constructor(), // 构造函数
method(code())); // 普通方法
JavaFile javaFile = JavaFile.builder("com.walfud.howtojavapoet", clazz).build();

System.out.println(javaFile.toString());
}

/**
* `public abstract class Clazz<T> extends String implements Serializable, Comparable<String>, Comparable<? extends String> {
* ...
* }`
*
* @return
*/
public static TypeSpec clazz(FieldSpec builtinTypeField, FieldSpec arrayTypeField, FieldSpec refTypeField,
FieldSpec typeField, FieldSpec parameterizedTypeField, FieldSpec wildcardTypeField,
MethodSpec constructor, MethodSpec methodSpec) {
return TypeSpec.classBuilder("Clazz")
// 限定符
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
// 泛型
.addTypeVariable(TypeVariableName.get("T"))

// 继承与接口
.superclass(String.class)
.addSuperinterface(Serializable.class)
.addSuperinterface(ParameterizedTypeName.get(Comparable.class, String.class))
.addSuperinterface(ParameterizedTypeName.get(ClassName.get(Map.class),
TypeVariableName.get("T"),
WildcardTypeName.subtypeOf(String.class)))

// 初始化块
.addStaticBlock(CodeBlock.builder().build())
.addInitializerBlock(CodeBlock.builder().build())

// 属性
.addField(builtinTypeField)
.addField(arrayTypeField)
.addField(refTypeField)
.addField(typeField)
.addField(parameterizedTypeField)
.addField(wildcardTypeField)

// 方法 (构造函数也在此定义)
.addMethod(constructor)
.addMethod(methodSpec)

// 内部类
.addType(TypeSpec.classBuilder("InnerClass").build())

.build();
}

/**
* 内置类型
*/
public static FieldSpec builtinTypeField() {
// private int mInt;
return FieldSpec.builder(int.class, "mInt", Modifier.PRIVATE).build();
}

/**
* 数组类型
*/
public static FieldSpec arrayTypeField() {
// private int[] mArr;
return FieldSpec.builder(int[].class, "mArr", Modifier.PRIVATE).build();
}

/**
* 需要导入 import 的类型
*/
public static FieldSpec refTypeField() {
// private File mRef;
return FieldSpec.builder(File.class, "mRef", Modifier.PRIVATE).build();
}

/**
* 泛型
*/
public static FieldSpec typeField() {
// private File mT;
return FieldSpec.builder(TypeVariableName.get("T"), "mT", Modifier.PRIVATE).build();
}

/**
* 参数化类型
*/
public static FieldSpec parameterizedTypeField() {
// private List<String> mParameterizedField;
return FieldSpec.builder(ParameterizedTypeName.get(List.class, String.class),
"mParameterizedField",
Modifier.PRIVATE)
.build();
}

/**
* 通配符参数化类型
*
* @return
*/
public static FieldSpec wildcardTypeField() {
// private List<? extends String> mWildcardField;
return FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(List.class),
WildcardTypeName.subtypeOf(String.class)),
"mWildcardField",
Modifier.PRIVATE)
.build();
}

/**
* 构造函数
*/
public static MethodSpec constructor() {
return MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.build();
}

/**
* `@Override
* public <T> Integer method(String string, T t, Map<Integer, ? extends T> map) throws IOException, RuntimeException {
* ...
* }`
*
* @param codeBlock
* @return
*/
public static MethodSpec method(CodeBlock codeBlock) {
return MethodSpec.methodBuilder("method")
.addAnnotation(Override.class)
.addTypeVariable(TypeVariableName.get("T"))
.addModifiers(Modifier.PUBLIC)
.returns(int.class)
.addParameter(String.class, "string")
.addParameter(TypeVariableName.get("T"), "t")
.addParameter(ParameterizedTypeName.get(ClassName.get(Map.class),
ClassName.get(Integer.class),
WildcardTypeName.subtypeOf(TypeVariableName.get("T"))),
"map")
.addException(IOException.class)
.addException(RuntimeException.class)
.addCode(codeBlock)
.build();
}

/**
* ‘method’ 方法中的具体语句
*/
public static CodeBlock code() {
return CodeBlock.builder()
.addStatement("int foo = 1")
.addStatement("$T bar = $S", String.class, "a string")

// Object obj = new HashMap<Integer, ? extends T>(5);
.addStatement("$T obj = new $T(5)",
Object.class, ParameterizedTypeName.get(ClassName.get(HashMap.class),
ClassName.get(Integer.class),
WildcardTypeName.subtypeOf(TypeVariableName.get("T"))))

// method(new Runnable(String param) {
// @Override
// void run() {
// }
// });
.addStatement("baz($L)", TypeSpec.anonymousClassBuilder("$T param", String.class)
.superclass(Runnable.class)
.addMethod(MethodSpec.methodBuilder("run")
.addAnnotation(Override.class)
.returns(TypeName.VOID)
.build())
.build())

// for
.beginControlFlow("for (int i = 0; i < 5; i++)")
.endControlFlow()

// while
.beginControlFlow("while (false)")
.endControlFlow()

// do... while
.beginControlFlow("do")
.endControlFlow("while (false)")

// if... else if... else...
.beginControlFlow("if (false)")
.nextControlFlow("else if (false)")
.nextControlFlow("else")
.endControlFlow()

// try... catch... finally
.beginControlFlow("try")
.nextControlFlow("catch ($T e)", Exception.class)
.addStatement("e.printStackTrace()")
.nextControlFlow("finally")
.endControlFlow()

.addStatement("return 0")
.build();
}
}

为了方便记忆, 我总结了一张图, 标注了关键或者稍微复杂的那些用法:

image

注意: 在 Android 工程中, 是无法使用 javax.lang.model.element.Modifier 包, 因为 Android 运行时没有相关的 class.


如果您喜欢这篇博客或发现它对您有用,则欢迎发表评论。 也欢迎您分享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢!