JavaPoet 动态生成Java源码---Android
Posted 独立开发者
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaPoet 动态生成Java源码---Android相关的知识,希望对你有一定的参考价值。
简介
官方描述: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
或保存以下二维码进行微信扫描:
以上是关于JavaPoet 动态生成Java源码---Android的主要内容,如果未能解决你的问题,请参考以下文章