前言
JavaPoet is a Java API for generating .java source files.
JavaPoet
是一个用于生成 .java
源文件的 Java API
, 在许多如 ButterKnift
、ARoute
等使用了 APT 技术的开源库中,都可以看到 JavaPoet
的影子。在解析注解生成 Java 源文件的过程中,JavaPoet
起的作用很大。
上图是 JavaPoet
库的文件结构:
AnnotationSpec
用于生成注解相关的 API;FieldSpec
用于生成属性变量字段相关的 API;MethodSpec
用于生成类的构造器、或者方法相关的 API;ParameterSpec
用于生成方法参数相关的 API;TypeSpec
用于生成class
类、接口、或者enum
相关的 API;className
用于声明在.java
文件中通过import
关键字导入
例子
在 Android Studio 中新建一个 Java 库,然后在 .gradle
文件中添加 JavaPoet
的依赖,效果如下:
在了解 JavaPoet
生成 .java
文件的规则之前,有必要先了解 JavaPoet
的字符串格式化规则:
符号 | 意义 |
---|---|
$L |
表示字面量,如: int value = $L |
$S |
表示字符串 |
$T |
可以表示类、接口 |
$N |
表示变量、函数名,和 $S 区分,主要是 $N 有实际的意义 |
# 例子_1
如下,如果我们知道我们要生成的类格式如下:
1 | package com.example.helloworld; |
通过 JavaPoet
库,我们可以通过如下 API :
1 | // 生成 main 方法 |
效果如下:
# 例子_2
1 | void main() { |
可以通过 JavaPoet
生成:
1 | MethodSpec mainMethod = MethodSpec.methodBuilder("main") |
对于上面的 beginControlFlow()
、nextControlFlow()
、endControlFlow()
, 一般可以用在循环、异常捕获、条件判断等。注意,在循环、异常捕获、条件判断的花括号最后要加上 endControlFlow()
。
# 例子_3
ClassName类型它可以标识任何声明的类。声明的类型只是Java丰富类型系统的开始:我们还具有数组,参数化类型,通配符类型和类型变量。JavaPoet具有用于构建以下每个类的类:
1 | ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard"); |
生成的 .java
文件如下:
1 | package com.example.helloworld; |
# 例子_4
JavaPoet支持导入静态。它通过显式收集类型成员名称来实现。
1 | ... |
生成的 .java
文件如下:
1 | package com.example.helloworld; |
# $N
生成的代码通常是自引用的。使用 $N
通过其名称引用另一个生成的声明。这是一个调用另一个的方法:
1 | public String byteToHex(int b) { |
在生成上面的代码时,我们使用 $N
将 hexDigit()
方法作为参数传递给 byteToHex()
方法:
1 | MethodSpec hexDigit = MethodSpec.methodBuilder("hexDigit") |
# 构造函数
使用 MethodSpec
也可以生成类的构造函数。
1 | MethodSpec flux = MethodSpec.constructorBuilder() |
生成的代码如下:
1 | public class HelloWorld { |
# 参数
使用 ParameterSpec.builder()
或 MethodSpec
的 API addParameter()
在方法和构造函数上声明参数:
1 |
|
生成的代码如下:
1 | void welcomeOverlords(final String android, final String robot) { |
# 字段
使用 FieldSpec#builder()
或 TypeSpec#addField()
生成字段。
1 | FieldSpec android = FieldSpec.builder(String.class, "android") |
生成的代码如下:
1 | public class HelloWorld { |
# 接口
对于 JavaPoet
的接口,请注意,接口方法必须始终为 PUBLIC ABSTRACT
,并且接口字段必须始终为 PUBLIC STATIC FINAL
。定义接口时,必须使用以下修饰符:
1 | TypeSpec helloWorld = TypeSpec.interfaceBuilder("HelloWorld") |
但是,在生成代码时将省略这些修饰符。
1 | public interface HelloWorld { |
# 枚举
1 | TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo") |
生成的代码如下:
1 | public enum Roshambo { |
小结
JavaPoet
还有挺多细节的生成规则,建议感兴趣的小伙伴可以阅读 JavaPoet
的官方文档。