javapoet的基础使用

/ 0评 / 1

前言

JavaPoet,顾名思义是Java诗人,可以用来为我们生成模板代码,通过Java代码去生成Java代码,是不是觉得很神奇,本篇博客主要介绍其基础使用,更多信息可以查看其Github简介以及本文的附录部分。

引入

implementation 'com.squareup:javapoet:1.11.1'

基本对象

JavaPoet将一个Java文件的不同部分使用不同的对象去表示,分别如下,当我们需要生成代码的时候,直接通过相关的对象去查找即可。

TypeSpec 代表类、接口
MethodSpec 代表方法
FieldSpec 代表成员变量
CodeBlock 代表代码块
JavaFile 代表一个Java文件,可以指定保存位置、包名等

生成代码

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);

上面的代码运行的结果为

package com.example.helloworld;

public final class HelloWorld {
  public static void main(String[] args) {
    System.out.println("Hello, JavaPoet!");
  }
}

首先我们使用MethodSpec.methodBuilder去创建一个名字叫main的方法,然后通过addModifiers()我们可以给方法添加标识符,通过returns(),我们可以添加返回值,通过addParameter(),用来添加参数。类似方法还有如下

addCode()    //添加一句代码
addAnnotation()    //添加一个注解
addException()    //添加一个异常
addComment()    //添加一个注释

然后我们使用TypeSpec.classBuilder去生成了一个名字叫HelloWorld的class,要想生成interface可以使用如下的方法。

TypeSpec.interfaceBuilder() //生成接口
TypeSpec.anonymousClassBuilder() //生成匿名类
TypeSpec.enumBuilder()  //生成枚举

然后通过addMethod将上面构造好的方法加入到TypeSpec中,则一个类就组装好了,当然,如果我们想为这个类加入成员变量,可以如下操作,通过FieldSpec.builder生成一个属性,然后add进去

FieldSpec fieldSpec = FieldSpec.builder(int.class, "i", Modifier.PUBLIC).build();

TypeSpec mainClass = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .addField(fieldSpec)
                .build();

最后则是JavaFile.builder("包名", TypeSpec)去生成Java文件,你可以自己去保存,通过toString()获得字符串,也可以通过writeTo通过传递File等自动生成java文件,就这么简单。

表达式的生成

要想生成表达式,有如下几种方法,addCode(),addStatement()

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;
  }
}

可以看到,我们直接使用addCode硬塞了一段代码,对于其中的变量,我们可以使用占位符代替,这个后面介绍。

MethodSpec mainMethod = MethodSpec.methodBuilder("main")
        .addStatement("$T.out.println($S)", System.class, "hello wolrd")
        .build();

生成结果

void main() {
    System.out.println("hello wolrd");
}

这里我们使用的addStatement添加的代码,并且使用了占位符。

addCodeaddStatement的区别就是addStatement会自动帮你引入需要的包和自动缩进。

循环代码生成

在上面的例子中,我介绍了使用addCode去添加整个循环语句,当然我们也可以使用addStatement,但是JavaPoet为我们提供了更好的方式。

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

生成

void main() {
  int total = 0;
  for (int i = 0; i < 10; i++) {
    total += i;
  }
}

我们可以使用beginControlFlow以及endControlFlow去设置循环开始语句以及循环结束,在这两句之间,我们可以使用addCode或者addStatement去添加循环体语句。

参数生成

在我们使用MethodSpec的时候,往往需要添加参数,这里详细介绍下如何添加参数。

方式一

 MethodSpec
        .methodBuilder("methodName")
        .addParameter(int.class, "abc")

我们直接使用一个类型的class作为参数,对应的类型是java.lang.reflect.Type

方式二

MethodSpec
        .methodBuilder("methodName")
        .addParameter(ClassName.get("android.content","Context"), "context")

我们直接用ClassName.get方法去构造一个class,这样比方法一要好在,有时候我们的javaPoet可能访问不到某个对象,比如Android里面的Context等。

占位符

$L 表示常量。可以使用数字或者字符串去填充。

$S 表示字符串。

$T 表示Type。使用Class去填充

$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();

生成

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');
}

参考链接:

JavaPoet Github

简书

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注