JavaPoet是用于生成.java源文件的库 。通过自动生成代码,可以替代许多重复的工作。在安卓项目中添加依赖时,凡是需要添加apt、kpt、annotationProcessor这种前缀标识时,说明其内部使用到了注解处理器,也基本可以确定使用到了JavaPoetKotlinPoet,例如大名鼎鼎的ButterknifeDagger就使用到了JavaPoet

我在两三年前也有使用注解处理器和JavaPoet写过一个Saber的库,在前一阵的维护中,突然对JavaPoet有些生疏所以想着记录总结一下,以便以后使用时快速上手。

1.准备

添加依赖:

dependencies {...implementation 'com.squareup:javapoet:1.13.0'
}

2.基本使用

既然是生成java文件,那么需要的基本元素就包括:

  • 创建字段(属性)
  • 创建方法
  • 创建类、接口或枚举
  • 输出文件

我们就按上面的顺序来一次介绍:

FieldSpec

FieldSpec就是用来创建字段的类,使用起来很简单:

FieldSpec.builder(String.class, "android").build()

以上代码等价于String android;

如果添加修饰符可以使用addModifiers,例如:

FieldSpec.builder(String.class, "android").addModifiers(Modifier.PRIVATE, Modifier.FINAL).build()
// private final String android;

其中Modifier包括我们用到的所有修饰符,下面介绍的类,方法都是使用它:

public enum Modifier {/** The modifier {@code public} */          PUBLIC,/** The modifier {@code protected} */       PROTECTED,/** The modifier {@code private} */         PRIVATE,/** The modifier {@code abstract} */        ABSTRACT,/*** The modifier {@code default}* @since 1.8*/DEFAULT,/** The modifier {@code static} */          STATIC,/** The modifier {@code final} */            FINAL,/** The modifier {@code transient} */       TRANSIENT,/** The modifier {@code volatile} */        VOLATILE,/** The modifier {@code synchronized} */    SYNCHRONIZED,/** The modifier {@code native} */          NATIVE,/** The modifier {@code strictfp} */        STRICTFP;
}

如果需要初始化参数,使用initializer,例如:

FieldSpec.builder(String.class, "android").addModifiers(Modifier.PRIVATE, Modifier.FINAL).initializer("$S", "Android").build()
// private final String android = "Android";

MethodSpec

MethodSpec用来创建方法,一个最简单的方法包含方法名、返回类型。

MethodSpec.methodBuilder("test").returns(void.class).build()
//  void test() {}

添加修饰符同上,这里不重复说明,如果添加方法参数,需要使用addParameter

MethodSpec.methodBuilder("test").addModifiers(Modifier.PUBLIC).addParameter(String.class, "str").returns(void.class).build()/*public void test(String str) {
}*/

最后就是给方法添加语句,需要使用addStatement

MethodSpec.methodBuilder("test").addModifiers(Modifier.PUBLIC).addParameter(String.class, "str").addStatement("System.out.println(str)").returns(void.class).build()/*public void test(String str) {System.out.println(str);
}*/

TypeSpec

TypeSpec用来创建类,接口,或者枚举。

// class Test {}
TypeSpec.classBuilder("Test").build();//interface Test {}
TypeSpec.interfaceBuilder("Test").build();/*
enum Test {ONE
}*/
TypeSpec typeSpec = TypeSpec.enumBuilder("Test").addEnumConstant("ONE").build();

JavaFile

JavaFile用于输出包含单个顶级类的Java文件。

JavaFile.builder("com.weilu.test", typeSpec).build();

用法很简单,指定包名和顶级类即可。

到这里,我们就可以将上面的用法串起来了:

class Test {public static void main(String[] args) {FieldSpec fieldSpec = FieldSpec.builder(String.class, "android").addModifiers(Modifier.PRIVATE, Modifier.FINAL).initializer("$S", "Android").build();MethodSpec methodSpec = MethodSpec.methodBuilder("test").addModifiers(Modifier.PUBLIC).addParameter(String.class, "str").returns(void.class).addStatement("System.out.println(str)").build();TypeSpec typeSpec = TypeSpec.classBuilder("Test").addField(fieldSpec).addMethod(methodSpec).build();JavaFile javaFile = JavaFile.builder("com.weilu.test", typeSpec).build();System.out.println(javaFile.toString());}
}

输出结果如下:

package com.weilu.test;import java.lang.String;class Test {private final String android = "Android";public void test(String str) {System.out.println(str);}
}

3.进阶用法

前面的用例过于简单,实际情况生成的代码是大量且复杂的。例如实现一个for循环:

MethodSpec.methodBuilder("main").addCode(""+ "int total = 0;\n"+ "for (int i = 0; i < 10; i++) {\n"+ "  total += i;\n"+ "}\n").build();

可以看到使用addCode方法,可以一股脑的添加所有代码。但是我们需要自己换行,输入分号和缩进,这明显是个费力不讨好的方式,所以我个人不推荐使用。

ControlFlow

使用addStatement 可以帮我们添加分号和换行,而使用beginControlFlowendControlFlow组合可以帮我们轻松实现控制流代码。所以上面的代码等价于:

MethodSpec.methodBuilder("main").addStatement("int total = 0").beginControlFlow("for (int i = 0; i < 10; i++)").addStatement("total += i").endControlFlow().build();

其他的使用场景,我举一些例子,大家一看便知:

// do... while
.beginControlFlow("do")
.endControlFlow("while (true)")// if... else if... else...
.beginControlFlow("if (true)")
.nextControlFlow("else if (false)")
.nextControlFlow("else")
.endControlFlow()// try... catch... finally
.beginControlFlow("try")
.nextControlFlow("catch ($T e)", Exception.class)
.addStatement("e.printStackTrace()")
.nextControlFlow("finally")
.endControlFlow()

占位符

上面的例子中,我们的代码都是固定的字符串,显得不灵活。因此JavaPoet为我们提供了多种占位符来满足要求。

$S (String)

当代码中包含字符串的时候, 可以使用 $S 表示。

private static MethodSpec whatsMyName(String name) {return MethodSpec.methodBuilder(name).returns(String.class).addStatement("return $S", name).build();
}

$L (Literal)

$L 是字面量替换,它与$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();
}

$T (Type)

上面例子为了简单,都使用的是一些基础类型,为的是不需要导包。实际中我们需要使用大量对象,如果只是在字符串中写死,代码虽没有问题,但是没有导包还是会保错。这是可以考虑使用$T,它的作用是替换类型。

MethodSpec today = MethodSpec.methodBuilder("today").returns(Date.class).addStatement("return new $T()", Date.class).build();

$N ( Name)

$N是名称替换。例如我们定义了一个getXXX的方法,我们调用它时可以使用addStatement("get$L()", "XXX")
这种写法实现,但是每次拼接"get"未免太麻烦了,一个不留心说不定还忘记了。那么使用addStatement("$N()", methodSpec)就更加方便了。

MethodSpec methodSpec = MethodSpec.methodBuilder("get" + name).returns(String.class).addStatement("return $S", name).build();MethodSpec.methodBuilder("getValue").returns(String.class).addStatement("return $N()", methodSpec).build();

继承与接口

TypeSpec.classBuilder("Test").superclass(String.class).addSuperinterface(Serializable.class).build();//class Test extends String implements Serializable {}

泛型

FieldSpec.builder(TypeVariableName.get("T"), "mT", Modifier.PRIVATE).build();
// private T mT;TypeVariableName mTypeVariable = TypeVariableName.get("T");
ParameterizedTypeName mListTypeName = ParameterizedTypeName.get(ClassName.get(List.class), mTypeVariable);
FieldSpec fieldSpec = FieldSpec.builder(mListTypeName, "mList", Modifier.PRIVATE).build();//private List<T> mList;

方法和类中使用addTypeVariable添加泛型。

初始化块

TypeSpec.classBuilder("Test").addStaticBlock(CodeBlock.builder().build()).addInitializerBlock(CodeBlock.builder().build()).build();/*
class Test {static {}{}
}*/

构造方法

MethodSpec methodSpec = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).build();TypeSpec typeSpec = TypeSpec.classBuilder("Test").addMethod(methodSpec).build();/*
class Test {public Test() {}
}*/

Annotations

添加注解的方法可以直接使用addAnnotation

MethodSpec toString = MethodSpec.methodBuilder("toString").addAnnotation(Override.class).returns(String.class).addModifiers(Modifier.PUBLIC).addStatement("return $S", "Hoverboard").build();

如果需要给注解设置属性,那么需要使用AnnotationSpec :

AnnotationSpec.builder(Headers.class).addMember("accept", "$S", "application/json; charset=utf-8").addMember("userAgent", "$S", "Square Cash").build()/*@Headers(accept = "application/json; charset=utf-8",userAgent = "Square Cash"
)*/

匿名内部类

TypeSpec.anonymousClassBuilder("") // <- 也可添加参数.superclass(Runnable.class).addMethod(MethodSpec.methodBuilder("run").addModifiers(Modifier.PUBLIC).addAnnotation(Override.class).returns(TypeName.VOID).build()).build();/*
new Runnable() {@Overridepublic void run() {}
}*/

Javadoc

添加注释可以使用addJavadoc,直接传入注释字符串就行了,具体就不说明了。


大体上就这么多了,文末的参考文章也非常全面,推荐阅读。如果本篇对你有所帮助,希望可以一键三连!

参考

  • JavaPoet 看这一篇就够了

JavaPoet使用攻略相关推荐

  1. Android 注解处理器使用攻略

    上一篇写了JavaPoet使用攻略,了解了JavaPoet用法.那么我们就可以结合今天的Annotation Processing Tool(APT)来自定义注解处理器. 注解处理器简单解释就是收集我 ...

  2. VIM配置攻略(最强干货加强版)

    自己最近也在折腾这个VIM的配置,在网上也看了很多教程等,说真的看的一头雾水.主要是对于一个初学者来说对Vundle等根本没有什么了解,也不知道怎么用,并且由于本人的Linux系统是CentOs6.5 ...

  3. 基于DCMTK的DICOM相关程序编写攻略

    2008年09月10日 星期三 15:35 基于DCMTK的DICOM相关程序编写攻略 前言: 由于现在的医学影像设备的图像存储和传输正在逐渐向DICOM标准靠拢,在我们进行医学图像处理的过程中,经常 ...

  4. http状态404 vscode_VS Code 调试完全攻略(5):基于浏览器的 React 应用

    每日前端夜话第344篇 翻译:疯狂的技术宅 作者:Charles Szilagyi 来源:charlesagile 正文共:1750 字 预计阅读时间:7 分钟 这次我们来研究怎样把调试器连接到用 C ...

  5. Java - 框架之 SpringBoot 攻略day01

    Spring-Boot 攻略 day01 spring-boot 一. 基本配置加运行 1. 导入配置文件(pom.xml 文件中) <parent> <groupId>org ...

  6. AWS攻略——使用CodeBuild进行自动化构建和部署Lambda(Python)

    Aws Lambda是Amazon推出的"无服务架构"服务.我们只需要简单的上传代码,做些简单的配置,便可以使用.而且它是按运行时间收费,这对于低频访问的服务来说很划算.具体的介绍 ...

  7. AWS攻略——使用CodeBuild进行自动化构建和部署静态网页

    首先声明下,使用"CodeBuild"部署并不是"正统"的方案,因为AWS提供了"CodeDeploy".如果不希望引入太多基础设施,可以考 ...

  8. 小黑盒不显示服务器,steam上买的游戏小黑盒不显示 | 手游网游页游攻略大全

    发布时间:2018-04-18 贪吃蛇遇上打方块是一款最近非常热门的休闲小游戏,游戏将贪吃蛇和打方块合而为一吸引了很多玩家!但是好多小伙们都不知道该怎么玩,下面小编来教你一个快速入门的小技巧吧!~ 小 ...

  9. 一般熟练盲打需要多久_进口攻略!一般货物进口清关需要多久?如何有效提高清关效率?...

    ***请点击上方红色的"关注"我们,您的关注,是我们努力的动力!谢谢!*** 一.清关是什么意思? 在回答"清关一般要多久之前"我们一起来看下,就是清关是什么意 ...

最新文章

  1. python使用selenium
  2. linux入门(二)
  3. centOS安装python
  4. WebStorm 10支持TypeScript 1.4到JavaScript的实时编译
  5. Hystrix Health Indicator及Metrics Stream支持
  6. linux 本地yum 恢复,Linux_RHEL系统恢复安装光盘中的yum更新源的方法,安装光盘本地YUM更新源挂载安 - phpStudy...
  7. linux里面启用无线网卡,linux启用无线网卡上网
  8. Exchange Server 2013日记功能
  9. mongodb数据库常用指令
  10. 2.3.3单链表的双向链表
  11. java与python难度对比_Python和Java的区别,看完这篇文章你就清楚啦
  12. NOIP2016-D2-T2 蚯蚓(单调队列)
  13. rabbitmq丢消息的处理方法
  14. asp.net在动态网页中的使用技巧
  15. Qt网络编程-简易版UDP单播通信入门Demo(3)
  16. 命令行解压cab文件
  17. Xshell使用教程
  18. ROS中使用A星算法进行路径规划
  19. 简历编辑导出工具(类似wps简历助手)
  20. 什么叫操作系统啊 | 战术后仰

热门文章

  1. 网络安全——防火墙配置实验
  2. python实现繁体中文和简体中文的互相转化
  3. nnUNet 训练 AMOS22数据集 Task216(抽丝剥茧指令+原理篇)
  4. 【汇编语言】快速理解什么是寻址,什么是寻址方式
  5. HighlightingSystem(边缘发光插件)的简单使用(一)
  6. 【CSS面试题】—CSS实现三角形
  7. 关于google smtp邮件发送
  8. PyQt5入门学习(一)【PyQt5及PyQt5-tools的安装】
  9. 基于MPLAB X IDE配置位设置讲解
  10. 什么是CC攻击?CC攻击的防御措施有哪些?