jdk
android studio
javapoet
请阅读android:[2]代码生成器Javapoet中的环境搭建说明
Javapoet的自动导入功能:android:[2]代码生成器Javapoet中生成的代码我们发现都自动导入了需要引用的类和变量要实现自动导入功能需要注意一点就是涉及到需要导入的地方不要用字符串拼接的方式拼接代码。应尽量使用class,ClassName 和Javapoet提供的几个特殊变量$N,$S,$L,$T
在介绍$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();显然第二种实现更优雅,不用我们关心分号,换行和缩进,可读性也更好
分别打印两中方式的输出信息System.out.println(main1.toString());System.out.println(main2.toString());编译运行后查看结果发现打印的结果一样
通常我们编写代码时会将通用的代码封装起来,更方便我们编码。这里我们就可以将如下类似代码的生成封装成一个方法。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;}
对于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 LiteralsThe 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();
对于字符串javapoet也提供了$SWhen 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());
对于类型,javapoet提供$T$T for TypesWe 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;导入语句
同时javapoet也支持静态导入静态导入通过JavaFile.builder的三个方法支持
$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'被映射成了方法名
对于匿名内部类也可以使用 $LTypeSpec 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
文章中所有源代码:https://git.oschina.net/jackyanngo/JavaPoetSample.git