阅读完需:约 18 分钟
JavaPoet是square推出的开源java代码生成框架,提供Java Api生成.java源文件。这个框架功能非常有用,我们可以很方便的使用它根据注解、数据库模式、协议格式等来对应生成代码。不直接操作字节码而是生成代码文件。
JavaPoet 之外还有 KotlinPoet,其实KotlinPoet和JavaPoet差不多的,只要会了JavaPoet那么KotlinPoet就没什么问题,就是写法改一下而已。
JavaPoet文档:
https://github.com/square/javapoet/
KotlinPoet文档:
https://square.github.io/kotlinpoet/#constructors
Java文件模型

Maven
<dependency>
<groupId>com.squareup</groupId>
<artifactId>javapoet</artifactId>
<version>1.13.0</version>
</dependency>
例子
想要生成以下代码
package com.example.helloworld;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
}
这是使用 JavaPoet 生成它的代码:
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
javaFile.writeTo(System.out);
为了声明 main 方法,我们创建了一个MethodSpec
配置有修饰符、返回类型、参数和代码语句的“main”。我们将 main 方法添加到HelloWorld
类中,然后将其添加到HelloWorld.java
文件中。
在这种情况下,我们将文件写入System.out
,但我们也可以将其作为字符串 ( JavaFile.toString()
) 获取或将其写入文件系统 ( JavaFile.writeTo()
)。
JavaPoet的常用类
类名 | 备注 |
---|---|
TypeSpec | 用于生成类、接口、枚举对象的类 |
MethodSpec | 用于生成方法对象的类 |
ParameterSpec | 用于生成参数对象的类 |
AnnotationSpec | 用于生成注解对象的类 |
FieldSpec | 用于配置生成成员变量的类 |
ClassName | 通过包名和类名生成的对象,在JavaPoet中相当于为其指定Class |
ParameterizedTypeName | 通过MainClass和IncludeClass生成包含泛型的Class |
JavaFile | 控制生成的Java文件的输出的类 |
JavaPoet的常用方法
方法名称 | 备注 |
---|---|
addModifiers(Modifier… modifiers) | 设置修饰关键字 |
addAnnotation | 设置注解对象 |
addJavadoc | 设置注释 |
JavaPoet生成方法
- 构造方法
MethodSpec.constructorBuilder()
- 常规方法
MethodSpec.methodBuilder(String name)
- 方法参数
addParameter(ParameterSpec parameterSpec)
- 返回值
returns(TypeName returnType)
设置方法体内容有两个方法,分别是addCode
和addStatement
:
addCode()
addStatement()
addCode
和addStatement
方法都存在这样的一个重载:
addCode(String format, Object... args)
addStatement(String format, Object... args)
可以用来设置模版
addCode
MethodSpec main = MethodSpec.methodBuilder("main")
.addCode(""
+ "int total = 0;\n"
+ "for (int i = 0; i < 10; i++) {\n"
+ " total += i;\n"
+ "}\n")
.build();
生成代码
void main() {
int total = 0;
for (int i = 0; i < 10; i++) {
total += i;
}
}
addStatement
其中addStatement()
处理分号和换行符,而 beginControlFlow()
+endControlFlow()
用于大括号、换行符和缩进。
对于 beginControlFlow()
、nextControlFlow()
、endControlFlow()
, 一般可以用在循环、异常捕获、条件判断等。注意,在循环、异常捕获、条件判断的花括号最后要加上 endControlFlow()
。
MethodSpec main = MethodSpec.methodBuilder("main")
.addStatement("int total = 0")
.beginControlFlow("for (int i = 0; i < 10; i++)")
.addStatement("total += i")
.endControlFlow()
.build();
这个例子很糟糕,因为生成的代码是不变的!假设我们不只是将 0 加到 10,而是要使操作和范围可配置。这是一个生成方法的方法:
private MethodSpec computeRange(String name, int from, int to, String op) {
return MethodSpec.methodBuilder(name)
.returns(int.class)
.addStatement("int result = 1")
.beginControlFlow("for (int i = " + from + "; i < " + to + "; i++)")
.addStatement("result = result " + op + " i")
.endControlFlow()
.addStatement("return result")
.build();
}
我们调用时得到的结果
int multiply10to20() {
int result = 1;
for (int i = 10; i < 20; i++) {
result = result * i;
}
return result;
}
方法生成方法!由于 JavaPoet 生成源代码而不是字节码,因此您可以通读它以确保它是正确的。
一些控制流语句,例如if/else
,可以有无限的控制流可能性。您可以使用以下方法处理这些选项nextControlFlow()
:
MethodSpec main = MethodSpec.methodBuilder("main")
.addStatement("long now = $T.currentTimeMillis()", System.class)
.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()
.build();
生成:
void main() {
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");
}
}
使用捕获异常try/catch
也是一个用例nextControlFlow()
:
MethodSpec main = MethodSpec.methodBuilder("main")
.beginControlFlow("try")
.addStatement("throw new Exception($S)", "Failed")
.nextControlFlow("catch ($T e)", Exception.class)
.addStatement("throw new $T(e)", RuntimeException.class)
.endControlFlow()
.build();
生成
void main() {
try {
throw new Exception("Failed");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
占位符
符号 | 意义 |
---|---|
$L |
表示字面量,如: int value = $L
|
$S |
表示字符串 |
$T |
可以表示类、接口 代指的是TypeName,该模板主要将Class抽象出来,用传入的TypeName指向的Class来代替 |
$N |
表示变量、函数名,和 $S 区分,主要是 $N 有实际的意义例如调用的方法名称,变量名称,这一类存在意思的名称 |
占位符的使用如下:
$L 表示文字
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();
}
文字直接发送到输出代码,没有转义。
$S 用于字符串
public static void main(String[] args) throws Exception {
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(whatsMyName("slimShady"))
.addMethod(whatsMyName("eminem"))
.addMethod(whatsMyName("marshallMathers"))
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
javaFile.writeTo(System.out);
}
private static MethodSpec whatsMyName(String name) {
return MethodSpec.methodBuilder(name)
.returns(String.class)
.addStatement("return $S", name)
.build();
}
生成代码
public final class HelloWorld {
String slimShady() {
return "slimShady";
}
String eminem() {
return "eminem";
}
String marshallMathers() {
return "marshallMathers";
}
}
$T 类型
对类型具有丰富的内置支持,包括自动生成import
语句。只需用于$T
引用类型:
MethodSpec today = MethodSpec.methodBuilder("today")
.returns(Date.class)
.addStatement("return new $T()", Date.class)
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(today)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build();
javaFile.writeTo(System.out);
代码生成
package com.example.helloworld;
import java.util.Date;
public final class HelloWorld {
Date today() {
return new Date();
}
}
我们通过Date.class
引用一个在我们生成代码时恰好可用的类。
如果一个目前还不存在可以通过以下方式进行import导入(ClassName.get)
ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");
MethodSpec today = MethodSpec.methodBuilder("tomorrow")
.returns(hoverboard)
.addStatement("return new $T()", hoverboard)
.build();
生成那个不存在类的代码
package com.example.helloworld;
import com.mattel.Hoverboard;
public final class HelloWorld {
Hoverboard tomorrow() {
return new Hoverboard();
}
}
ClassName.get
它可以识别任何声明的类。声明类型只是 Java 丰富类型系统的开始:我们还有数组、参数化类型、通配符类型和类型变量。JavaPoet 有用于构建这些的类:
ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");
ClassName list = ClassName.get("java.util", "List");
ClassName arrayList = ClassName.get("java.util", "ArrayList");
TypeName listOfHoverboards = ParameterizedTypeName.get(list, hoverboard);
MethodSpec beyond = MethodSpec.methodBuilder("beyond")
.returns(listOfHoverboards)
.addStatement("$T result = new $T<>()", listOfHoverboards, arrayList)
.addStatement("result.add(new $T())", hoverboard)
.addStatement("result.add(new $T())", hoverboard)
.addStatement("result.add(new $T())", hoverboard)
.addStatement("return result")
.build();
生成代码
package com.example.helloworld;
import com.mattel.Hoverboard;
import java.util.ArrayList;
import java.util.List;
public final class HelloWorld {
List<Hoverboard> beyond() {
List<Hoverboard> result = new ArrayList<>();
result.add(new Hoverboard());
result.add(new Hoverboard());
result.add(new Hoverboard());
return result;
}
}
导入静态
JavaPoet 支持import static
. 它通过显式收集类型成员名称来实现。
...
ClassName namedBoards = ClassName.get("com.mattel", "Hoverboard", "Boards");
MethodSpec beyond = MethodSpec.methodBuilder("beyond")
.returns(listOfHoverboards)
.addStatement("$T result = new $T<>()", listOfHoverboards, arrayList)
.addStatement("result.add($T.createNimbus(2000))", hoverboard)
.addStatement("result.add($T.createNimbus(\"2001\"))", hoverboard)
.addStatement("result.add($T.createNimbus($T.THUNDERBOLT))", hoverboard, namedBoards)
.addStatement("$T.sort(result)", Collections.class)
.addStatement("return result.isEmpty() ? $T.emptyList() : result", Collections.class)
.build();
TypeSpec hello = TypeSpec.classBuilder("HelloWorld")
.addMethod(beyond)
.build();
JavaFile.builder("com.example.helloworld", hello)
.addStaticImport(hoverboard, "createNimbus")
.addStaticImport(namedBoards, "*")
.addStaticImport(Collections.class, "*")
.build();
代码生成
package com.example.helloworld;
import static com.mattel.Hoverboard.Boards.*;
import static com.mattel.Hoverboard.createNimbus;
import static java.util.Collections.*;
import com.mattel.Hoverboard;
import java.util.ArrayList;
import java.util.List;
class HelloWorld {
List<Hoverboard> beyond() {
List<Hoverboard> result = new ArrayList<>();
result.add(createNimbus(2000));
result.add(createNimbus("2001"));
result.add(createNimbus(THUNDERBOLT));
sort(result);
return result.isEmpty() ? emptyList() : result;
}
}
$N 用于名称
生成的代码通常是自引用的。用于$N
通过名称引用另一个生成的声明。这是一个调用另一个的方法:
public String byteToHex(int b) {
char[] result = new char[2];
result[0] = hexDigit((b >>> 4) & 0xf);
result[1] = hexDigit(b & 0xf);
return new String(result);
}
public char hexDigit(int i) {
return (char) (i < 10 ? i + '0' : i - 10 + 'a');
}
在生成上面的代码时,我们将hexDigit()
方法作为参数传递给byteToHex()
方法,使用$N
:
MethodSpec hexDigit = MethodSpec.methodBuilder("hexDigit")
.addParameter(int.class, "i")
.returns(char.class)
.addStatement("return (char) (i < 10 ? i + '0' : i - 10 + 'a')")
.build();
MethodSpec byteToHex = MethodSpec.methodBuilder("byteToHex")
.addParameter(int.class, "b")
.returns(String.class)
.addStatement("char[] result = new char[2]")
.addStatement("result[0] = $N((b >>> 4) & 0xf)", hexDigit)
.addStatement("result[1] = $N(b & 0xf)", hexDigit)
.addStatement("return new String(result)")
.build();
代码块格式字符串
代码块可以通过几种方式为其占位符指定值。代码块上的每个操作只能使用一种样式。
将格式字符串中每个占位符的参数值传递给CodeBlock.add()
CodeBlock.builder().add("I ate $L $L", 3, "tacos")
在格式字符串中的占位符之前放置一个整数索引(从 1 开始)以指定要使用的参数。
CodeBlock.builder().add("I ate $2L $1L", "tacos", 3)
使用包含格式字符串中所有参数键的映射调用。
Map<String, Object> map = new LinkedHashMap<>();
map.put("food", "tacos");
map.put("count", 3);
CodeBlock.builder().addNamed("I ate $count:L $food:L", map)
方法
MethodSpec flux = MethodSpec.methodBuilder("flux")
.addModifiers(Modifier.ABSTRACT, Modifier.PROTECTED)
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addMethod(flux)
.build();
这会产生:
public abstract class HelloWorld {
protected abstract void flux();
}
构造函数
MethodSpec
有点用词不当;它也可以用于构造函数:
MethodSpec flux = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(String.class, "greeting")
.addStatement("this.$N = $N", "greeting", "greeting")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC)
.addField(String.class, "greeting", Modifier.PRIVATE, Modifier.FINAL)
.addMethod(flux)
.build();
产生代码
public class HelloWorld {
private final String greeting;
public HelloWorld(String greeting) {
this.greeting = greeting;
}
}
在大多数情况下,构造函数就像方法一样工作。发出代码时,JavaPoet 会将构造函数放在输出文件中的方法之前。
参数
使用 ParameterSpec.builder()
或 MethodSpec
的 API addParameter()
在方法和构造函数上声明参数:
ParameterSpec android = ParameterSpec.builder(String.class, "android")
.addModifiers(Modifier.FINAL)
.build();
MethodSpec welcomeOverlords = MethodSpec.methodBuilder("welcomeOverlords")
.addParameter(android)
.addParameter(String.class, "robot", Modifier.FINAL)
.build();
虽然上面生成的代码android
和robot
参数不同,但输出是一样的:
void welcomeOverlords(final String android, final String robot) {
}
Builder
当参数有注解(如)时,扩展形式是必需的@Nullable
。
字段变量属性
与参数一样,可以使用构建器或使用方便的辅助方法创建字段:
FieldSpec android = FieldSpec.builder(String.class, "android")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC)
.addField(android)
.addField(String.class, "robot", Modifier.PRIVATE, Modifier.FINAL)
.build();
生成
public class HelloWorld {
private final String android;
private final String robot;
}
有初始值
FieldSpec android = FieldSpec.builder(String.class, "android")
.addModifiers(Modifier.PRIVATE, Modifier.FINAL)
.initializer("$S + $L", "Lollipop v.", 5.0d)
.build();
生成
private final String android = "Lollipop v." + 5.0;
接口
JavaPoet 在接口方面没有问题。请注意,接口方法必须始终为PUBLIC ABSTRACT
,接口字段必须始终为PUBLIC STATIC FINAL
. 这些修饰符在定义接口时是必需的:
TypeSpec helloWorld = TypeSpec.interfaceBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC)
.addField(FieldSpec.builder(String.class, "ONLY_THING_THAT_IS_CONSTANT")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer("$S", "change")
.build())
.addMethod(MethodSpec.methodBuilder("beep")
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.build())
.build();
但是在生成代码时这些修饰符被省略了。这些是默认值,所以我们不需要为了javac
的利益而包含它们!
public interface HelloWorld {
String ONLY_THING_THAT_IS_CONSTANT = "change";
void beep();
}
枚举
用于enumBuilder
创建枚举类型,并addEnumConstant()
为每个值:
TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo")
.addModifiers(Modifier.PUBLIC)
.addEnumConstant("ROCK")
.addEnumConstant("SCISSORS")
.addEnumConstant("PAPER")
.build();
生成
public enum Roshambo {
ROCK,
SCISSORS,
PAPER
}
支持花哨的枚举,其中枚举值覆盖方法或调用超类构造函数。这是一个综合示例:
TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo")
.addModifiers(Modifier.PUBLIC)
.addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("$S", "fist")
.addMethod(MethodSpec.methodBuilder("toString")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addStatement("return $S", "avalanche!")
.returns(String.class)
.build())
.build())
.addEnumConstant("SCISSORS", TypeSpec.anonymousClassBuilder("$S", "peace")
.build())
.addEnumConstant("PAPER", TypeSpec.anonymousClassBuilder("$S", "flat")
.build())
.addField(String.class, "handsign", Modifier.PRIVATE, Modifier.FINAL)
.addMethod(MethodSpec.constructorBuilder()
.addParameter(String.class, "handsign")
.addStatement("this.$N = $N", "handsign", "handsign")
.build())
.build();
生成
public enum Roshambo {
ROCK("fist") {
@Override
public String toString() {
return "avalanche!";
}
},
SCISSORS("peace"),
PAPER("flat");
private final String handsign;
Roshambo(String handsign) {
this.handsign = handsign;
}
}
匿名内部类
在枚举代码中,我们使用了TypeSpec.anonymousInnerClass()
. 匿名内部类也可以在代码块中使用。它们是可以引用的值$L
:
TypeSpec comparator = TypeSpec.anonymousClassBuilder("")
.addSuperinterface(ParameterizedTypeName.get(Comparator.class, String.class))
.addMethod(MethodSpec.methodBuilder("compare")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addParameter(String.class, "a")
.addParameter(String.class, "b")
.returns(int.class)
.addStatement("return $N.length() - $N.length()", "a", "b")
.build())
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addMethod(MethodSpec.methodBuilder("sortByLength")
.addParameter(ParameterizedTypeName.get(List.class, String.class), "strings")
.addStatement("$T.sort($N, $L)", Collections.class, "strings", comparator)
.build())
.build();
这会生成一个方法,该方法包含一个包含方法的类:
void sortByLength(List<String> strings) {
Collections.sort(strings, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
}
定义匿名内部类的一个特别棘手的部分是超类构造函数的参数。在上面的代码中,我们传递了没有参数的空字符串: TypeSpec.anonymousClassBuilder("")
. 要传递不同的参数,请使用 JavaPoet 的代码块语法,用逗号分隔参数。
注解
MethodSpec toString = MethodSpec.methodBuilder("toString")
.addAnnotation(Override.class)
.returns(String.class)
.addModifiers(Modifier.PUBLIC)
.addStatement("return $S", "Hoverboard")
.build();
@Override
它使用注释生成此方法:
@Override
public String toString() {
return "Hoverboard";
}
用于AnnotationSpec.builder()
设置注释的属性:
MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent")
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addAnnotation(AnnotationSpec.builder(Headers.class)
.addMember("accept", "$S", "application/json; charset=utf-8")
.addMember("userAgent", "$S", "Square Cash")
.build())
.addParameter(LogRecord.class, "logRecord")
.returns(LogReceipt.class)
.build();
accept
它使用和userAgent
属性生成此注释:
@Headers(
accept = "application/json; charset=utf-8",
userAgent = "Square Cash"
)
LogReceipt recordEvent(LogRecord logRecord);
当您喜欢时,注释值可以是注释本身。用于$L
嵌入注释:
MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent")
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addAnnotation(AnnotationSpec.builder(HeaderList.class)
.addMember("value", "$L", AnnotationSpec.builder(Header.class)
.addMember("name", "$S", "Accept")
.addMember("value", "$S", "application/json; charset=utf-8")
.build())
.addMember("value", "$L", AnnotationSpec.builder(Header.class)
.addMember("name", "$S", "User-Agent")
.addMember("value", "$S", "Square Cash")
.build())
.build())
.addParameter(LogRecord.class, "logRecord")
.returns(LogReceipt.class)
.build();
产生
@HeaderList({
@Header(name = "Accept", value = "application/json; charset=utf-8"),
@Header(name = "User-Agent", value = "Square Cash")
})
LogReceipt recordEvent(LogRecord logRecord);
可以addMember()
使用相同的属性名称多次调用以填充该属性的值列表。
Javadoc
字段、方法和类型可以用 Javadoc 记录:
MethodSpec dismiss = MethodSpec.methodBuilder("dismiss")
.addJavadoc("Hides {@code message} from the caller's history. Other\n"
+ "participants in the conversation will continue to see the\n"
+ "message in their own history unless they also delete it.\n")
.addJavadoc("\n")
.addJavadoc("<p>Use {@link #delete($T)} to delete the entire\n"
+ "conversation for all participants.\n", Conversation.class)
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.addParameter(Message.class, "message")
.build();
产生
/**
* Hides {@code message} from the caller's history. Other
* participants in the conversation will continue to see the
* message in their own history unless they also delete it.
*
* <p>Use {@link #delete(Conversation)} to delete the entire
* conversation for all participants.
*/
void dismiss(Message message);
$T在 Javadoc 中引用类型以获取自动导入时使用。
完整案例
/**
* 我们在用 APT (Annotation Processing Toolkit) 或者 KAPT 在编译期生成源代码时,
* 一般都会用 JavaPoet 来生成 Java 源代码,而用 KotlinPoet 来生成 Kotlin 源代码,乍一看,哇!好酷,感觉特别有逼格。
* @author xujiahui
*/
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.enmalvi.javapoet", 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();
}
}
产生
package com.enmalvi.javapoet;
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 {
}
}