博客搬遷到這里 http://blog.fdawei.club,歡迎訪問,大家一起學習交流。
前面通過一篇博客介紹了注解和Apt,今天介紹一個有意思的開源庫javapoet。
poet意思是詩人,很形象的名稱,沒錯,就像詩人能寫出優美的詩句一樣,它能夠優雅的自動生成java代碼。下面就來一步一步解開它神秘的面紗。
javapoet用法
按照慣例,我們先使用它寫一個輸出Hello World的類??蠢?/p>
FieldSpec fieldSpec = FieldSpec.builder(String.class, "helloWorld", Modifier.PRIVATE)
.initializer("\"Hello World!\"").build();
MethodSpec methodSpec = MethodSpec.methodBuilder("printHelloWorld")
.addModifiers(Modifier.PUBLIC)
.addCode("System.out.println(helloWorld)")
.build();
TypeSpec classSpec = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC)
.addField(fieldSpec)
.addMethod(methodSpec)
.build();
JavaFile javaFile = JavaFile.builder("me.fdawei", classSpec).build();
System.out.print(javaFile.toString());
運行結果
可以看見,確實已經幫我們生成了需要的類。下面詳細看看實現過程。
常用類
先介紹幾個常用類??疵挚赡艽蠹揖鸵呀浤軌虿碌剿麄兪鞘裁戳?。
- FieldSpec 代表一個成員變量,一個字段聲明。
- MethodSpec 代表一個構造函數或方法聲明。
- TypeSpec 代表一個類,接口,或者枚舉聲明。
- ParameterSpec 用來創建參數。
- JavaFile包含一個頂級類的Java文件。
- AnnotationSpec 用來創建注解。
- TypeName 類型名,如在添加返回值類型是使用 TypeName.VOID
- ClassName 用來包裝一個類,提供類名。
添加修飾關鍵字
Modifier是用來設置一些修飾關鍵字的,如,public、final、static等等,看其取值就一目了然。FieldSpec、MethodSpec、TypeSpec中都可以使用。
生成成員變量
FieldSpec fieldSpec = FieldSpec.builder(String.class, "helloWorld", Modifier.PRIVATE)
.initializer("\"Hello World!\"").build();
使用initializer方法,可以在定義成員變量時對其添加初始化代碼。這里添加了一個String類型的成員變量helloWorld,并將它初始化為字符串 “Hellow World!”。
生成成員方法
MethodSpec methodSpec = MethodSpec.methodBuilder("printHelloWorld")
.addModifiers(Modifier.PUBLIC)
.addCode("System.out.println(helloWorld)")
.build();
生成Public類型的方法printHelloWorld,實現輸出helloWorld變量的值。
addCode方法的使用比較簡單。javapoet中,除了使用addCode以字符串拼湊的方式添加代碼執行邏輯,javapoet還提供了更加優雅的方式。
MethodSpec methodSpec = MethodSpec.methodBuilder("printHelloWorld")
.addModifiers(Modifier.PUBLIC)
.addStatement("System.out.println(helloWorld)")
.build();
addStatement添加一行執行代碼,并自動在末尾添加“;”和換行。如果是for、while、if這樣的控制結構,可以使用beginControlFlow和endControlFlow來實現
MethodSpec methodSpec = MethodSpec.methodBuilder("printHelloWorld")
.addModifiers(Modifier.PUBLIC)
.beginControlFlow("for(int i = 0; i < 5; i++)")
.addStatement("System.out.println(helloWorld)")
.endControlFlow()
.build();
很明顯,這里使用for循環輸出五次"Hello World!"。
生成類
TypeSpec classSpec = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC)
.addField(fieldSpec)
.addMethod(methodSpec)
.build();
生成名為 HelloWorld 的類,并添加了一個成員變量和一個方法。
TypeSpec創建類時,如果需要生成實現了接口或繼承其他類的類怎么辦?TypeSpec提供addSuperinterface方法和superclass方法供使用。
生成Java源文件
JavaFile javaFile =
JavaFile.builder("me.fdawei", classSpec).build();
builder方法的第一個參數制定了源文件的包名,第二個參數就是需要生成的類的信息。
占位符
使用FieldSpec生成成員變量時,可以通過initializer方法為其設置初始化代碼,例子中就使用字符串“Hello World!”來初始化String類型的成員變量helloWorld。這樣問題就來了,如果使用字符串拼接的話,字符串中需要包含引號,這樣就非常難處理了,需要進行轉義。就是這樣的寫法
FieldSpec fieldSpec = FieldSpec.builder(String.class, "helloWorld", Modifier.PRIVATE)
.initializer("\"Hello World!\"").build();
這樣話顯得很麻煩,如果字符串復雜的話,很容易出錯。這就要用到占位符了。這里可以使用$S來進行占位。
FieldSpec fieldSpec = FieldSpec.builder(String.class, "helloWorld", Modifier.PRIVATE)
.initializer("$S", "Hello World!").build();
$S用來給一個字符串占位。除了$S之外,javapoet中還有三個占位符,分別是:
- $L 用來給一個數字占位
- $T 用來給一個類,接口,或者枚舉占位
- $N 用來給我們自己生成的方法名或者變量名等占位
有了占位符,生成成員方法時我們也可以這樣寫
MethodSpec methodSpec = MethodSpec.methodBuilder("printHelloWorld")
.addModifiers(Modifier.PUBLIC)
.addCode("$T.out.println(helloWorld);\n", System.class)
.build();
比較運行結果
看到不同了沒有,對,就是多了import java.lang.System。說明單純的字符串,javapoet只會原樣添加到代碼中,使用了占位符$T,則會被視為一種類型,會自動導入對應的包。
如果你想靜態導入,當然也是可以的,使用addStaticImport方法即可。
JavaFile javaFile = JavaFile.builder("me.fdawei", classSpec)
.addStaticImport(Modifier.class, "*").build();
除了最基本的這些方法來組裝代碼外,javapoet還提供很多其他的添加不同元素的方法
- TypeSpec.enumBuilder() 生成枚舉類型
- TypeSpec.interfaceBuilder() 生成接口
- MethodSpec.addJavadoc() 給方法添加注釋
- MethodSpec.constructorBuilder() 創建構造方法
- MethodSpec.addAnnotation() 添加注解
其實javapoet可以做的還不止這些,在以后的使用中可以慢慢發掘。