android:[3]代码生成器Javapoet
1、请阅读android:[2]代码生成器Javapoet中的环境搭建说明
1、Javapoet的自动导入功能:
android:[2]代码生成器Javapoet中生成的代码我们发现都自动导入了需要引用的类和变量
要实现自动导入功能需要注意一点就是涉及到需要导入的地方不要用字符串拼接的方式拼接代码。应尽量使用class,ClassName 和Javapoet提供的几个特殊变量
$N,$S,$L,$T
2、在介绍$N,$S,$L,$T这几个变量之前先看看Javapoet中的流程控制
如果我们要生成如下代码:
void main() { int total = 0; for (int i = 0; i < 10; i++) { total += i; }}
我们可以通过如下两种方式实现
MethodSpec main1 = MethodSpec.methodBuilder("main") .addCode("" + "int total = 0;\n" + "for (int i = 0; i < 10; i++) {\n" + " total += i;\n" + "}\n") .build();
MethodSpec main2 = MethodSpec.methodBuilder("main") .addStatement("int total = 0") .beginControlFlow("for (int i = 0; i < 10; i++)") .addStatement("total += i") .endControlFlow() .build();
显然第二种实现更优雅,不用我们关心分号,换行和缩进,可读性也更好
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/18aebc5f0c14c27be9bea43c2a46b7b1eff93991.jpg)
3、分别打印两中方式的输出信息
System.out.println(main1.toString());System.out.println(main2.toString());
编译运行后查看结果发现打印的结果一样
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/35f2224133bad3414541c382427622bc7cc52c91.jpg)
4、通常我们编写代码时会将通用的代码封装起来,更方便我们编码。
这里我们就可以将如下类似代码的生成封装成一个方法。
MethodSpec gennerageMethodSpec(String name, int from, int to, String op) {
return MethodSpec.methodBuilder(name) .returns(int.class) .addStatement("int result = 0") .beginControlFlow("for (int i = " + from + "; i < " + to + "; i++)") .addStatement("result = result " + op + " i") .endControlFlow() .addStatement("return result") .build();}
如果我们要生成
int plus() {
int result = 0;
for (int i = 1; i < 100; i++) {
result = result + i;
}
return result;
}
这样的代码只需调用gennerageMethodSpec("plus", 1, 100, "+")即可
如果我们调用gennerageMethodSpec("multiply", 1, 100, "*")
则会生成如下代码
int multiply() {
int result = 0;
for (int i = 1; i < 100; i++) {
result = result * i;
}
return result;
}
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/2a1ecb460596b814a4ec97b643d246fe464e2291.jpg)
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/3aae2b4f50b8b43ef3e775ff7132939c2df71991.jpg)
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/dd58d02c5b1b1ede91e0828c981fceecd2d90f91.jpg)
5、对于
MethodSpec gennerageMethodSpec(String name, int from, int to, String op) {
return MethodSpec.methodBuilder(name) .returns(int.class) .addStatement("int result = 0") .beginControlFlow("for (int i = " + from + "; i < " + to + "; i++)") .addStatement("result = result " + op + " i") .endControlFlow() .addStatement("return result") .build();}
方法,我们依然采用了字符串拼接的方式,其实javapoet提供的$L来处理 Literals
$L for Literals
The string-concatenation in calls to beginControlFlow() and addStatement is distracting. Too many operators. To address this, JavaPoet offers a syntax inspired-by but incompatible-with String.format(). It accepts $L to emit a literal value in the output. This works just like Formatter's %s:
MethodSpec gennerageMethodSpec$L(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();
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/58021a0148fe1e422c572664c2299a8838130391.jpg)
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/bf6e59704618dfda9907193389214f5792567791.jpg)
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/974a2f21056104a3dcd5a11b63d7592ae2ef6b91.jpg)
6、对于字符串javapoet也提供了$S
When emitting code that includes string literals, we can use $S to emit a string, complete with wrapping quotation marks and escaping. Here's a program that emits 3 methods, each of which returns its own name:
static MethodSpec whatsMyName(String name) { return MethodSpec.methodBuilder(name) .returns(String.class) .addStatement("return $S", name) .build();}
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();System.out.println(javaFile.toString());
输出结果为:
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();System.out.println(javaFile.toString());
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/562787cf02532f63b5b9c283699147e832e05c91.jpg)
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/6002c9d4483104eb529b7dba092b74ee1d324e91.jpg)
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/4a594f2c8cf1d8a78b0f6a3146e34b2c57ee4791.jpg)
7、对于类型,javapoet提供$T
$T for Types
We Java programmers love our types: they make our code easier to understand. And JavaPoet is on board. It has rich built-in support for types, including automatic generation of import statements. Just use $T to reference types:
通过 $T 进行映射,会自动import声明
MethodSpec today = MethodSpec.methodBuilder("today") .returns(Date.class) .addStatement("return new $T()", Date.class) .build();TypeSpec dataNow = TypeSpec.classBuilder("DataNow") .addModifiers(Modifier.PUBLIC, Modifier.FINAL) .addMethod(today) .build();JavaFile javaFile_datanow = JavaFile.builder("com.example.generate", dataNow) .build();System.out.println(javaFile_datanow.toString());
生成的代码中自动添加了import java.util.Date;导入语句
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/7d34fbf4fcf5ee0d82f28862f96b0ce264e7ba91.jpg)
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/bff8683e21c2bbd625ec4f5d116186254093ae91.jpg)
8、同时javapoet也支持静态导入
静态导入通过JavaFile.builder的三个方法支持
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/555acf0ff2260d9afe32352c622abab84340a591.jpg)
9、$N for Names
使用 $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);}
其中hexDigit也是方法
那么这里的hexDigit方法名即可用$N映射
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();System.out.println(byteToHex.toString());
可见最终传入的字符串"hexDigit"被映射成了方法名
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/f0848bee41c1b727766f243d1a2ca5cadde89891.jpg)
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/91091efc77f7980ebc1c61c4d4db3620b83a9191.jpg)
10、对于匿名内部类也可以使用 $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 comparate = TypeSpec.classBuilder("Comparate") .addMethod(MethodSpec.methodBuilder("sortByLength") .addParameter(ParameterizedTypeName.get(List.class, String.class), "strings") .addStatement("$T.sort($N, $L)", Collections.class, "strings", comparator) .build()) .build();System.out.println(comparate.toString());
最终生成代码:
class Comparate {
void sortByLength(java.util.List<java.lang.String> strings) {
java.util.Collections.sort(strings, new java.util.Comparator<java.lang.String>() {
@java.lang.Override
public int compare(java.lang.String a, java.lang.String b) {
return a.length() - b.length();
}
});
}
}
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/ccc83ec5260f883531b67b6bce07880139708691.jpg)
![android:[3]代码生成器Javapoet](https://exp-picture.cdn.bcebos.com/a749bb0f94fc508c6cf2e54d01775ddd894cfd91.jpg)
11、文章中所有源代码:https://git.oschina.net/jackyanngo/JavaPoetSample.git