JavaPoet 动态生成Java源码(1)---Android
来源:互联网 发布:服务器域名是后缀吗 编辑:程序博客网 时间:2024/06/05 20:11
简介
官方描述:JavaPoet is a Java API for generating .java source files.
(JavaPoet是一个Java Api接口生成.java源码文件的工程)
JavaPoet源码:https://github.com/square/javapoet
我在此主要承担一个翻译解释的角色,分析给大家;为后一篇文章的发表,奠定一定的基础使用语法。
引入库
compile 'com.squareup:javapoet:1.7.0'
or Maven:
<dependency> <groupId>com.squareup</groupId> <artifactId>javapoet</artifactId> <version>1.7.0</version></dependency>
or Eclipse:
https://search.maven.org/remote_content?g=com.squareup&a=javapoet&v=LATEST
使用
当做一些事情,如注解处理或元数据处理例如,数据库模式,协议格式)时,源文件生成是有用处的。通过生成的代码,你需要同时保持正确的唯一来源的元数据模板。
例如
看一个简单无趣的栗子:
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文件。
在这个Case中,我们写这个文件到System.out进行输出,但我们可以把它作为字符串或写入到文件系统。
这个文档目录有所有的JavaPoet接口,我们继续向下探究学习。
代码与控制流
很多JavaPoet接口使用了会稳定的Java类。这样构建,方法链式和交互使Api更友好。JavaPoet经常使用的典型案例有类&接口(TypeSpec),属性(FieldSpec),方法&构造方法(MethodSpec),参数(ParameterSpec)和注解(AnnotationSpec).
但是方法和构造方法的主题并没有建模。没有表达式类,没有语句类或语法树节点。相反,javapoet使用字符串的代码块:
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; }}
我们人输入分号,换行符和缩进,这些繁琐的的事情使用JavaPoet提供的接口使这些变得更容易。
这个.addStatement()方法负责分号和换行,beginControlFlow() + 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 = 0") .beginControlFlow("for (int i = " + from + "; i < " + to + "; i++)") .addStatement("result = result " + op + " i") .endControlFlow() .addStatement("return result") .build();}
下方就是我们执行上方代码computeRange(“multiply10to20”, 10, 20, “*”)得到以下结果:
int multiply10to20() { int result = 0; for (int i = 10; i < 20; i++) { result = result * i; } return result;}
方法生成方法!因为JavaPoet生成的是源码不是字节码,你可以阅读它却确保是正确的。
对于文字:$L
连接字符串调用beginControlFlow() 和 addStatement是分散的。太多的操作者。为了解决这个,JavaPoet提供了一个语法灵感,但是不符合语法String.format()。它接受$L在输出中发出一个文字值。这就像是Formatter’s %s。
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();}
文字直接排放在输出语句中,参数可以是字符串,语句,和一些JavaPoet类型的数据。
对于字符串:$S
当输出的代码中包含字符串时,我们可以使用$S发送一个字符串,完成和包裹引用。下面是一个程序,它会发出3种方法,每一种方法都会返回自己的名字:
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();}
在这个Case中,$S给添加引号:
public final class HelloWorld { String slimShady() { return "slimShady"; } String eminem() { return "eminem"; } String marshallMathers() { return "marshallMathers"; }}
对于泛型的类型:$T
我们Java程序喜欢我们自己的类型:它们生成的代码很容易让我们理解。在JavaPoet上,它有丰富的内置支持类型,包含自动生成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);
生成一下.java文件,完成必要的import:
package com.example.helloworld;import java.util.Date;public final class HelloWorld { Date today() { return new Date(); }}
我们通过Date.class引用类,正好可以代码生成。这不需要是这样。这里有一个类似的例子,但这一个引用了一个不存在的类:
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类型是非常重要的,当你频繁使用JavaPoet时,你将需要它。它可以识别任何声明类。声明的类型是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();
JavaPoet将每一种类型分解,并在可能的情况下导入其组件。
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; }}
Import static
JavaPoet支持导入静态类。它通过显式收集类型成员名称。让我们以一些静态来举栗子:
...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();
JavaPoet首先将导入静态块来配置文件,也需要导入其他类型。
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();
下一篇,接着解析或翻译剩余的部分;
文章出处:
[Coolspan CSDN博客:http://blog.csdn.net/qxs965266509][4]
欢迎关注我的公众号,实时给你推送文章,谢谢支持;
微信搜索公众号:coolspan
或保存以下二维码进行微信扫描:
- Android JavaPoet 动态生成Java源码(1)
- JavaPoet 动态生成Java源码(1)---Android
- JavaPoet动态生成代码
- JavaPoet生成.java源代码
- JavaPoet源码初探
- javapoet:源文件自动生成框架
- JavaPoet
- JavaPoet
- JavaPoet
- Android关于AutoService、Javapoet讲解
- java源码动态生成编译,以及方法调用
- Android 通过Java代码生成创建界面。动态生成View,动态设置View属性。addRules详解
- Android 通过Java代码生成创建界面。动态生成View,动态设置View属性。addRules详解
- android 可以用来写代码的代码(JavaPoet)
- Java注解全解析(附)——javapoet简介
- Android动态生成UI
- Android 动态生成控件
- Android 动态生成控件
- 一种海量文章排重的算法
- 三个简短的动画实现饿了么红包滑动效果
- adb 命令行参数有空格
- [leetcode 464]Can I Win
- 第十三周练习--计算成绩
- JavaPoet 动态生成Java源码(1)---Android
- Android Camera 采集之无需界面
- Debug Assertion Failed
- jdk环境变量配置
- 真正从零开始,TensorFlow详细安装入门图文教程!
- wamp配置多个站点,访问类似127.0.0.2
- Gsonformat使用和bean最简单用法
- MYSQL学习笔记(1)
- iOS10 和 Xcode8 的改变