Java注释

JavaPoet是一个用于生成. Java源文件的Java API。

源文件生成在做注释处理或交互等事情时非常有用

使用元数据文件(例如,数据库模式,协议格式)。通过生成代码,可以消除

需要编写样板文件,同时为元数据保留一个真实的单一来源。

例子

这是一个(无聊的)HelloWorld类:

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”,并配置了修饰符return
类型、参数和代码语句。我们将main方法添加到HelloWorld类,然后添加
到HelloWorld.java文件。
在本例中,我们将文件写入System。Out,但我们也可以把它作为字符串
(JavaFile.toString()))或将其写入文件系统(JavaFile.writeTo())。
Javadoc对完整的JavaPoet API进行了编目,我们将在下面探讨。

代码和控制流程

大多数JavaPoet的API使用普通的旧的不可变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提供了api让它更简单。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 = 1")
      .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 = 1;
  for (int i = 10; i < 20; i++) {
    result = result * i;
  }
  return result;
}

方法生成方法!因为JavaPoet生成源代码而不是字节码,所以可以通读一遍,确保正确无误。一些控制流语句,如if/else,可以有无限的控制流可能性。你可以使用nextControlFlow()来处理这些选项:

MethodSpec main = MethodSpec.methodBuilder("main")
    .addStatement("long now = $T.currentTimeMillis()", System.class)
    .beginControlFlow("if ($T.currentTimeMillis() < now)", System.class)
    .addStatement("$T.out.println($S)", System.class, "Time travelling, woo hoo!")
    .nextControlFlow("else if ($T.currentTimeMillis() == now)", System.class)
    .addStatement("$T.out.println($S)", System.class, "Time stood still!")
    .nextControlFlow("else")
    .addStatement("$T.out.println($S)", System.class, "Ok, time still moving forward")
    .endControlFlow()
    .build();

生成:

void main() {
  long now = System.currentTimeMillis();
  if (System.currentTimeMillis() < now)  {
    System.out.println("Time travelling, woo hoo!");
  } else if (System.currentTimeMillis() == now) {
    System.out.println("Time stood still!");
  } else {
    System.out.println("Ok, time still moving forward");
  }
}

使用try/catch捕获异常也是nextControlFlow()的一个用例:

MethodSpec main = MethodSpec.methodBuilder("main")
    .beginControlFlow("try")
    .addStatement("throw new Exception($S)", "Failed")
    .nextControlFlow("catch ($T e)", Exception.class)
    .addStatement("throw new $T(e)", RuntimeException.class)
    .endControlFlow()
    .build();

生成:

void main() {
  try {
    throw new Exception("Failed");
  } catch (Exception e) {
    throw new RuntimeException(e);
  }
}

$L代表字面量

调用beginControlFlow()和addStatement中的字符串连接是分散注意力的。太许多运营商。为了解决这个问题,JavaPoet提供了一种受启发但不兼容的语法String.format()。它接受$L在输出中发出一个文字值。这就像Formatter的%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();
}

在本例中,使用$S会得到引号:

public final class HelloWorld {
  String slimShady() {
    return "slimShady";
  }

  String eminem() {
    return "eminem";
  }

  String marshallMathers() {
    return "marshallMathers";
  }
}

$T表示类型

我们Java程序员喜欢我们的类型:它们使我们的代码更容易理解。JavaPoet打开了董事会。它具有丰富的内置类型支持,包括自动生成导入语句。只需使用$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文件,并完成必要的导入:

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

静态导入语法

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

代码块格式字符串

代码块可以通过几种方式指定占位符的值。只能使用一种样式

对于代码块上的每个操作。

相对参数

将格式字符串中每个占位符的参数值传递给CodeBlock.add()。在每一个

例如,我们生成代码说“我吃了3个玉米饼”

CodeBlock.builder().add("I ate $L $L", 3, "tacos")

定位参数

在格式字符串中的占位符之前放置一个整数索引(基于1)以指定哪个

要使用的参数。

CodeBlock.builder().add("I ate $2L $1L", "tacos", 3)

命名参数

使用语法$argumentName:X,其中X是格式字符,并调用CodeBlock.addNamed()

使用包含格式字符串中所有参数键的映射。参数名中使用字符

a-z, a-z, 0-9, _,必须以小写字母开头。

Map<String, Object> map = new LinkedHashMap<>();
map.put("food", "tacos");
map.put("count", 3);
CodeBlock.builder().addNamed("I ate $count:L $food:L", map)

方法

以上所有方法都有一个代码体。使用修饰符。摘要:得到一种无任何问题的方法的身体。只有当外围类是抽象类或接口时,这才合法。

MethodSpec flux = MethodSpec.methodBuilder("flux")
    .addModifiers(Modifier.ABSTRACT, Modifier.PROTECTED)
    .build();

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
    .addMethod(flux)
    .build();

这就产生了这个:

public abstract class HelloWorld {
  protected abstract void flux();
}

其他修饰符在允许的情况下使用。注意,在指定修饰符时,JavaPoet使用

javax.lang.model.element。这个类在Android上是不可用的。这限制只适用于代码生成代码;输出代码可以在任何地方运行:jvm, Android,和GWT。

方法也有参数、异常、变参数、Javadoc、注释、类型变量和返回类型。所有这些都是用MethodSpec.Builder配置的。

构造函数

MethodSpec是一个轻微的用词不当;它也可以用于构造函数:

MethodSpec flux = MethodSpec.constructorBuilder()
    .addModifiers(Modifier.PUBLIC)
    .addParameter(String.class, "greeting")
    .addStatement("this.$N = $N", "greeting", "greeting")
    .build();

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC)
    .addField(String.class, "greeting", Modifier.PRIVATE, Modifier.FINAL)
    .addMethod(flux)
    .build();

这就产生了这个:

public class HelloWorld {
  private final String greeting;

  public HelloWorld(String greeting) {
    this.greeting = greeting;
  }
}

在大多数情况下,构造函数就像方法一样工作。当发出代码时,JavaPoet将放置

输出文件中方法前面的构造函数。

参数

在方法和构造函数上使用ParameterSpec.builder()或MethodSpec方便的addParameter() API:

ParameterSpec android = ParameterSpec.builder(String.class, "android")
    .addModifiers(Modifier.FINAL)
    .build();

MethodSpec welcomeOverlords = MethodSpec.methodBuilder("welcomeOverlords")
    .addParameter(android)
    .addParameter(String.class, "robot", Modifier.FINAL)
    .build();

虽然上面生成android和robot参数的代码是不同的,但输出的是相同:

void welcomeOverlords(final String android, final String robot) {
}

当参数有注释(比如@Nullable)时,扩展的Builder是表单是有必要的

字段

像参数一样,字段可以用构建器或使用方便的helper方法创建:

FieldSpec android = FieldSpec.builder(String.class, "android")
    .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
    .build();
    
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC)
    .addField(android)
    .addField(String.class, "robot", Modifier.PRIVATE, Modifier.FINAL)
    .build();

生成:

public class HelloWorld {
  private final String android;

  private final String robot;
}

当字段有Javadoc、注释或字段时,扩展的Builder表单是必要的初始化器。字段初始化器使用与代码相同的String.format()类语法块上图:

FieldSpec android = FieldSpec.builder(String.class, "android")
    .addModifiers(Modifier.PRIVATE, Modifier.FINAL)
    .initializer("$S + $L", "Lollipop v.", 5.0d)
    .build();

生成:

private final String android = "Lollipop v." + 5.0;

Interfaces

JavaPoet在接口方面没有问题。注意,接口方法必须始终是PUBLIC ABSTRACT,接口字段必须始终是PUBLIC STATIC FINAL。这些修饰词是必要的

定义接口时:

TypeSpec helloWorld = TypeSpec.interfaceBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC)
    .addField(FieldSpec.builder(String.class, "ONLY_THING_THAT_IS_CONSTANT")
        .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
        .initializer("$S", "change")
        .build())
    .addMethod(MethodSpec.methodBuilder("beep")
        .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
        .build())
    .build();

但是在生成代码时,这些修饰符将被省略。这些是默认值,所以我们不需要为了javac的利益而包含它们!

public interface HelloWorld {
  String ONLY_THING_THAT_IS_CONSTANT = "change";

  void beep();
}

枚举

使用enumBuilder创建枚举类型,并为每个值添加enumconstant ():

TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo")
    .addModifiers(Modifier.PUBLIC)
    .addEnumConstant("ROCK")
    .addEnumConstant("SCISSORS")
    .addEnumConstant("PAPER")
    .build();
```java

To generate this:
```java
public enum Roshambo {
  ROCK,

  SCISSORS,

  PAPER
}

支持花哨的枚举,其中枚举值覆盖方法或调用超类构造函数。这里有一个全面的例子:

TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo")
    .addModifiers(Modifier.PUBLIC)
    .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("$S", "fist")
        .addMethod(MethodSpec.methodBuilder("toString")
            .addAnnotation(Override.class)
            .addModifiers(Modifier.PUBLIC)
            .addStatement("return $S", "avalanche!")
            .returns(String.class)
            .build())
        .build())
    .addEnumConstant("SCISSORS", TypeSpec.anonymousClassBuilder("$S", "peace")
        .build())
    .addEnumConstant("PAPER", TypeSpec.anonymousClassBuilder("$S", "flat")
        .build())
    .addField(String.class, "handsign", Modifier.PRIVATE, Modifier.FINAL)
    .addMethod(MethodSpec.constructorBuilder()
        .addParameter(String.class, "handsign")
        .addStatement("this.$N = $N", "handsign", "handsign")
        .build())
    .build();

这就产生了这个:

public enum Roshambo {
  ROCK("fist") {
    @Override
    public String toString() {
      return "avalanche!";
    }
  },

  SCISSORS("peace"),

  PAPER("flat");

  private final String handsign;

  Roshambo(String handsign) {
    this.handsign = handsign;
  }
}

匿名内部类

在枚举代码中,我们使用了typesp . anonymousinnerclass()。中也可以使用匿名内部类代码块。它们是可以用$L引用的值:

TypeSpec 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 helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addMethod(MethodSpec.methodBuilder("sortByLength")
        .addParameter(ParameterizedTypeName.get(List.class, String.class), "strings")
        .addStatement("$T.sort($N, $L)", Collections.class, "strings", comparator)
        .build())
    .build();

这将生成一个方法,该方法包含一个包含方法的类:

void sortByLength(List<String> strings) {
  Collections.sort(strings, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
      return a.length() - b.length();
    }
  });
}

定义匿名内部类的一个特别棘手的部分是超类的参数构造函数。在上面的代码中,我们传递无参数的空字符串:

TypeSpec.anonymousClassBuilder(" ")。要传递不同的参数,请使用JavaPoet的代码块用逗号分隔参数的语法。

注释

简单的注释很简单:

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

它生成了一个@Override注释的方法:


  @Override
  public String toString() {
    return "Hoverboard";
  }

使用AnnotationSpec.builder()来设置注释的属性:

MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent")
    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
    .addAnnotation(AnnotationSpec.builder(Headers.class)
        .addMember("accept", "$S", "application/json; charset=utf-8")
        .addMember("userAgent", "$S", "Square Cash")
        .build())
    .addParameter(LogRecord.class, "logRecord")
    .returns(LogReceipt.class)
    .build();

生成带有accept和userAgent属性的注释:

@Headers(
    accept = "application/json; charset=utf-8",
    userAgent = "Square Cash"
)
LogReceipt recordEvent(LogRecord logRecord);

如果您喜欢的话,注释值本身也可以是注释。使用$L嵌入注释:

MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent")
    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
    .addAnnotation(AnnotationSpec.builder(HeaderList.class)
        .addMember("value", "$L", AnnotationSpec.builder(Header.class)
            .addMember("name", "$S", "Accept")
            .addMember("value", "$S", "application/json; charset=utf-8")
            .build())
        .addMember("value", "$L", AnnotationSpec.builder(Header.class)
            .addMember("name", "$S", "User-Agent")
            .addMember("value", "$S", "Square Cash")
            .build())
        .build())
    .addParameter(LogRecord.class, "logRecord")
    .returns(LogReceipt.class)
    .build();

这就产生了这个:

@HeaderList({
    @Header(name = "Accept", value = "application/json; charset=utf-8"),
    @Header(name = "User-Agent", value = "Square Cash")
})
LogReceipt recordEvent(LogRecord logRecord);

注意,您可以使用相同的属性名多次调用addMember()来填充列表属性的值。

Javadoc

字段、方法和类型可以用Javadoc记录:

MethodSpec dismiss = MethodSpec.methodBuilder("dismiss")
    .addJavadoc("Hides {@code message} from the caller's history. Other\n"
        + "participants in the conversation will continue to see the\n"
        + "message in their own history unless they also delete it.\n")
    .addJavadoc("\n")
    .addJavadoc("<p>Use {@link #delete($T)} to delete the entire\n"
        + "conversation for all participants.\n", Conversation.class)
    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
    .addParameter(Message.class, "message")
    .build();

这就产生了这个:

 /**
   * Hides {@code message} from the caller's history. Other
   * participants in the conversation will continue to see the
   * message in their own history unless they also delete it.
   *
   * <p>Use {@link #delete(Conversation)} to delete the entire
   * conversation for all participants.
   */
  void dismiss(Message message);

在Javadoc中引用类型以获得自动导入时使用$T。

下载

通过Maven下载最新的.jar或依赖:

<dependency>
  <groupId>com.squareup</groupId>
  <artifactId>javapoet</artifactId>
  <version>1.13.0</version>
</dependency>

或 Gradle:

compile 'com.squareup:javapoet:1.13.0'

开发版本的快照可以在Sonatype的快照存储库中找到。

JavaWriter

JavaPoet是JavaWriter的继承者。新项目应该更喜欢JavaPoet,因为

它具有更强的代码模型:它理解类型并可以自动管理导入。JavaPoet是

也更适合组合:而不是流化.java文件的内容

在单次传递中,一个文件可以被组装成一棵声明树。

JavaWriter仍可在GitHub和Maven Central中使用。

引用

一、JavaPoet[1]: https://gitcode.net/mirrors/square/javapoet?utm_source=csdn_github_accelerator
二、ButterKnife

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值