JavaPoet
目錄
- 簡(jiǎn)介
- 引入
- 簡(jiǎn)單范例
- JavaPoet的常用類
- 占位符
- $L for Literals
- $S for Strings
- $T for Types
- $N for Names
- 參數(shù)
- 相對(duì)參數(shù)
- 位置參數(shù)
- 名字參數(shù)
- 使用詳解
- 抽象方法
- 生成構(gòu)造方法
- 方法參數(shù)
- 類變量
- 接口
- 枚舉
- 匿名內(nèi)部類
- 注解
- Javadoc
簡(jiǎn)介
JavaPoet是一款可以自動(dòng)生成Java文件的第三方依賴。JavaPoet是JavaWriter的繼承者。新項(xiàng)目應(yīng)該首選JavaPoet,因?yàn)樗幸粋€(gè)更強(qiáng)大的代碼模型:它能理解類型并能自動(dòng)管理導(dǎo)入。JavaPoet也更適合于組合:與其將一個(gè).java文件的內(nèi)容自上而下地一次性流傳,不如將一個(gè)文件組裝成一棵聲明樹。
引入
這里介紹兩種項(xiàng)目中的引入方式
在maven項(xiàng)目中的引入方式:在pom.xml中添加
在Android項(xiàng)目中的引入方式:在需要使用Javapoet的gradle中添加
compile 'com.squareup:javapoet:1.13.0'其它Java語言的項(xiàng)目中應(yīng)該也是在相關(guān)配置文件中引入就行
簡(jiǎn)單范例
我們可以使用javapoet生成以下代碼
package com.example.helloJavaPoet; import java.lang.String; import java.lang.System; public final class HelloJavaPoet {public static void main(String[] args) {System.out.println("Hello, JavaPoet!");} }使用以下代碼可以生成上述代碼
import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.TypeSpec; import javax.lang.model.element.Modifier; import java.io.File; import java.io.FileWriter; import java.io.IOException;public class HelloWorld {public static void main(String[] args) {// 給類添加一個(gè)方法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 代表一個(gè)類TypeSpec helloWorld = TypeSpec.classBuilder("HelloJavaPoet").addModifiers(Modifier.PUBLIC, Modifier.FINAL).addMethod(main).build();// JavaFile 代表 Java 文件JavaFile javaFile = JavaFile.builder("com.example.helloJavaPoet", helloWorld).build();File outFile = new File("com/example/helloJavaPoet.java");if(!outFile.getParentFile().exists()) {outFile.getParentFile().mkdirs();}if (!outFile.exists()) {try {outFile.createNewFile();} catch (IOException e) {e.printStackTrace();}}FileWriter writer;try {writer = new FileWriter(outFile.getAbsolutePath());writer.write("");//清空原文件內(nèi)容writer.write(javaFile.toString());writer.flush();writer.close();} catch (IOException e) {e.printStackTrace();}} }
因?yàn)椴皇窃诎沧宽?xiàng)目中實(shí)現(xiàn)的,所以生成文件上復(fù)雜了點(diǎn),用了File和FileWriter
JavaPoet的常用類
JavaFile————用于生成的Java文件的類
TypeSpec————用于生成類、接口、枚舉對(duì)象的類
MethodSpec————用于生成方法的類
ParameterSpec————用于生成參數(shù)的類
AnnotationSpec————用于生成注解的類
FieldSpec————用于生成變量的類
ClassName————通過包名和類名生成的對(duì)象,在JavaPoet中相當(dāng)于為其指定Class
ParameterizedTypeName————通過MainClass和IncludeClass生成包含泛型的Class
占位符
$L for Literals
$L指的是沒有轉(zhuǎn)義的字面值,可以是字符串、基本數(shù)據(jù)類型、類型聲明、注解甚至其他代碼塊的占位符,就像Formatter的%s。
上面的代碼中,將$S換成$L就會(huì)不會(huì)轉(zhuǎn)換成字符串(就是不加引號(hào)),直接以字面量代替
$S for Strings
$S指的是可以帶著引號(hào)和轉(zhuǎn)義的字符串的占位符。
(這里和上面的轉(zhuǎn)義還沒弄的很懂,官方文檔中也只是給了引號(hào)的例子)
$T for Types
$T指的是數(shù)組類型的占位符,它不僅支持內(nèi)置類型,還包括自動(dòng)生成import語句。
例如:
用這個(gè)
生成這個(gè)
package com.example.helloworld;import java.util.Date;public final class HelloWorld {Date today() {return new Date();} }$N for Names
指的是一個(gè)名稱的占位符,例如調(diào)用的方法名稱,變量名稱(可以是自動(dòng)生成的方法和變量)。
例如:
用這個(gè)代碼
生成
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'); }參數(shù)
相對(duì)參數(shù)
CodeBlock.builder().add("I ate $L $L", 3, "tacos").build()生成字符串
I ate 3 tacos位置參數(shù)
CodeBlock.builder().add("I ate $2L $1L", "tacos", 3).build()生成字符串同上
名字參數(shù)
使用語法$argumentName:X,其中X是上述占位符中的字母,用包含格式字符串中所有參數(shù)鍵的映射調(diào)用CodeBlock.addNamed()。參數(shù)名使用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可以用來獲得一個(gè)沒有任何主體的方法。當(dāng)這只有在類是抽象類或接口時(shí)才合法。
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(); }生成構(gòu)造方法
普通方法用的是 MethodSpec.methodBuilder(方法名) 生成,構(gòu)造方法是用MethodSpec.constructorBuilder()生成
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;} }方法參數(shù)
用 ParameterSpec.builder() 或 ==MethodSpec的addParameter() ==在方法和構(gòu)造函數(shù)上聲明參數(shù)。
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(); void welcomeOverlords(final String android, final String robot) { }類變量
變量的生成和方法參數(shù)也是一樣的。用 FieldSpec.builder() 或 TypeSpec的addField()。這里不做解釋。
接口
JavaPoet生成接口,接口方法必須始終是PUBLIC ABSTRACT,接口字段必須始終是PUBLIC STATIC FINAL。這些修飾語在定義接口時(shí)是必要的
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();但這些修飾語在代碼生成時(shí)被省略了。這些都是默認(rèn)的,所以我們不需要為了javac而包含它們!
public interface HelloWorld {String ONLY_THING_THAT_IS_CONSTANT = "change";void beep(); }枚舉
使用enumBuilder來創(chuàng)建枚舉類型,并用 EnumConstant() 添加值。
TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo").addModifiers(Modifier.PUBLIC).addEnumConstant("ROCK").addEnumConstant("SCISSORS").addEnumConstant("PAPER").build();生成:
public enum Roshambo {ROCK,SCISSORS,PAPER }此外,javapoet支持花式枚舉,其中枚舉值覆蓋方法或調(diào)用超類構(gòu)造函數(shù)。這里有一個(gè)全面的例子
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") {@Overridepublic String toString() {return "avalanche!";}},SCISSORS("peace"),PAPER("flat");private final String handsign;Roshambo(String handsign) {this.handsign = handsign;} }匿名內(nèi)部類
在枚舉的代碼中,我們使用了TypeSpec.anonymousInnerClass()。匿名內(nèi)部類也可以在代碼塊中使用。它們是可以用$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>() {@Overridepublic int compare(String a, String b) {return a.length() - b.length();}}); }定義匿名內(nèi)類的一個(gè)特別棘手的部分是給超類構(gòu)造函數(shù)的參數(shù)。在上面的代碼中,我們傳遞的是空字符串,沒有參數(shù):TypeSpec.anonymousClassBuilder("")。要傳遞不同的參數(shù),請(qǐng)使用JavaPoet的代碼塊語法,用逗號(hào)來分隔參數(shù)。
注解
用 addAnnotation() 生成注解
例如:
MethodSpec toString = MethodSpec.methodBuilder("toString").addAnnotation(Override.class).returns(String.class).addModifiers(Modifier.PUBLIC).addStatement("return $S", "Hoverboard").build();生成代碼
@Overridepublic String toString() {return "Hoverboard";}使用AnnotationSpec.builder()來設(shè)置注釋的屬性。
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();生成代碼
@Headers(accept = "application/json; charset=utf-8",userAgent = "Square Cash" ) LogReceipt recordEvent(LogRecord logRecord);此外,注解的值可以是注解。使用$L進(jìn)行嵌入式注解。
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);注意,你可以用同一個(gè)屬性名稱多次調(diào)用addMember()來為該屬性填充一個(gè)值的列表。
Javadoc
字段、方法和類型可以用Javadoc進(jìn)行記錄
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);總結(jié)
- 上一篇: 喀什大坤云玺台是哪个开发商?
- 下一篇: 万事兴集成灶好不好?