JavaPoet 文件翻譯

PatrickGao發表於2018-02-19

JavaPoet

JavaPoet 是一套生成.java原始檔的Java介面。

當做一些比如註解處理或者和後設資料檔案(比如資料庫的schemas,協議格式)互動的事情時,原始檔生成非常有用。通過生成程式碼,你不用寫模板程式碼同時也保證了後設資料的唯一來源。

Example

下面是樣板式的 HelloWorld class:

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方法,我們用修飾符,返回型別,引數和程式碼語句建立一個名為"main"的MethodSpec。我們新增main方法到一個HelloWorld類中,然後新增這個類到HelloWorld.java檔案中。

在這個例子中我們把檔案寫到System.out中,但我們也能得到檔案的字串(JavaFile.toString()) 或把它寫到檔案系統裡(JavaPoet.writeTo())。

Javadoc是全部的JavaPoet API,我們接下來看一下。

程式碼和控制流

大多數的JavaPoet的API用簡單的不可變的Java物件。也有builders,方法鏈和可變引數來使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 = 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** 來傳送一個string,包含雙引號的包裹和轉義。下面是一個傳送三個方法的程式,每個都返回它自己的名字:

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也是同理。它有豐富的內建支援型別,包含自動生成import語句。使用**$T** 來引用 types:

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支援import static。它通過明確地收集型別成員名字來完成的。讓我們使用一些靜態語法糖來完善前一個例子:

...
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 will first add your import static block to the file as configured, match and mangle all calls accordingly and also import all other types as needed.

JavaPoet將先新增你的import static塊到檔案中作為配置,匹配和壓碎所有呼叫因此也引入所有需要的其他型別。(翻譯不通)

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');
}
複製程式碼

當生成上面的程式碼,我使用$N傳遞hexDigit()方法作為byteToHex()方法的一個引數:

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()。在每個例子中,我們生成程式碼來說"I ate 3 tacos"

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)
複製程式碼

方法

以上所有方法都有方法體。使用Modifiers.ABSTRACT可以獲取一個沒有方法體的方法。如果是抽象類或者介面的話則是合法的。

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.Modifier,這個類在Android不可用。這個限制只在程式碼生成的程式碼中有效;輸出的程式碼可以執行在各種地方:JVM,Android和GWT。

方法也有引數,異常,可變引數,Java文件,註解,型別變數和一個返回型別。所有這些都可以在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():

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();
複製程式碼

雖然上面程式碼生成androidrobot的引數不同,但是輸出是相同的:

void welcomeOverlords(final String android, final String robot) {
}
複製程式碼

當引數有註解(例如 @Nullable)時擴充套件的Builder型別是必要的。

成員變數

與引數類似,成員變數也是既能使用builder建立也能使用方便的幫助方法建立:

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;
複製程式碼

介面

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建立列舉型別,並且 addEnumConstant() 勇於新增每個值:

TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo")
    .addModifiers(Modifier.PUBLIC)
    .addEnumConstant("ROCK")
    .addEnumConstant("SCISSORS")
    .addEnumConstant("PAPER")
    .build();
複製程式碼

生成:

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!")
            .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 void toString() {
      return "avalanche!";
    }
  },

  SCISSORS("peace"),

  PAPER("flat");

  private final String handsign;

  Roshambo(String handsign) {
    this.handsign = handsign;
  }
}
複製程式碼

匿名內部類

在列舉程式碼中,我們使用Types.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();
複製程式碼

生成帶有acceptuserAgent屬性的註解:

@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來獲得自動匯入

Download

Download the latest .jar or depend via Maven:

<dependency>
  <groupId>com.squareup</groupId>
  <artifactId>javapoet</artifactId>
  <version>1.9.0</version>
</dependency>
複製程式碼

or Gradle:

compile 'com.squareup:javapoet:1.9.0'
複製程式碼

Snapshots of the development version are available in Sonatype's snapshots repository.

License

Copyright 2015 Square, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
複製程式碼

JavaWriter

JavaPoet is the successor to JavaWriter. New projects should prefer JavaPoet because it has a stronger code model: it understands types and can manage imports automatically. JavaPoet is also better suited to composition: rather than streaming the contents of a .java file top-to-bottom in a single pass, a file can be assembled as a tree of declarations.

JavaWriter continues to be available in GitHub and Maven Central.

相關文章