天天看點

javapoet——會寫代碼的“詩人”Javapoet

Javapoet

1. 簡介

1.1 什麼是javapoet

pote 即詩人,也就是寫文字的人,而javapet也就是使用java來作詩的人。而在Java的世界裡,“詩”就是java的源代碼。是以,javppoet就是來幫助我們來生成Java源代碼的工具。這一點在使用編譯時注解來幫助我們自動生成一些類提供了很大的周遊。下面就讓我們來看看這位詩人是如何來寫出優美的“詩句”的。

1.2 導入javapoet

本文是在android studio 環境下開發的,需要導入相應的依賴

compile 'com.squareup:javapoet:1.7.0'

2. javapoet 的使用

2.1 javapoet 中的相關知識點

2.1.1 幾個常用的類

  • MethodSpec 代表一個構造函數或方法聲明。
  • TypeSpec 代表一個類,接口,或者枚舉聲明
  • FieldSpec 代表一個成員變量,一個字段聲明。
  • JavaFile包含一個頂級類的Java檔案。
  • ParameterSpec 用來建立參數
  • AnnotationSpec 用來建立注解
  • TypeName 類型,如在添加傳回值類型是使用 TypeName.VOID
  • ClassName 用來包裝一個類

2.1.2 javapoet 常用的API

  • addStatement() 方法負責分号和換行
  • beginControlFlow() + endControlFlow() 需要一起使用,提供換行符和縮進。
  • addCode() 以字元串的形式添加内
  • returns 添加傳回值類型
  • .constructorBuilder() 生成構造器函數
  • .addAnnotation 添加注解
  • addSuperinterface 給類添加實作的接口
  • superclass 給類添加繼承的父類
  • ClassName.bestGuess(“類全名稱”) 傳回ClassName對象,這裡的類全名稱表示的類必須要存在,會自動導入相應的包
  • ClassName.get(“包名”,”類名”) 傳回ClassName對象,不檢查該類是否存在
  • TypeSpec.interfaceBuilder(“HelloWorld”)生成一個HelloWorld接口
  • MethodSpec.constructorBuilder() 構造器
  • addTypeVariable(TypeVariableName.get(“T”, typeClassName))

    會給生成的類加上泛型

2.1.3 占位符

  • $L代表的是字面量
  • $S for Strings
  • $N for Names(我們自己生成的方法名或者變量名等等)
  • $T for Types

這裡的$T,在生成的源代碼裡面,也會自動導入你的類。

這些點可以在閱讀下面的代碼時作為參考

2.2 入門 Hello World

通過javapoet 來生成一個帶有mian方法并且在mian方法中在控制台輸出”hello world” 的類

`
public static void main(String[] args) throws IOException {
    MethodSpec main = MethodSpec
            .methodBuilder("main") //添加方法的名稱
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC) //方法修飾的關鍵字
            .addParameter(String[].class, "args") //添加方法的參數
            .addStatement("$T.out.println($S)", System.class, "hello world") //添加代碼
            .build();
    TypeSpec hello = TypeSpec.classBuilder("HelloWorld") //添加類的名稱
            .addModifiers(Modifier.PUBLIC) //添加修飾的關鍵字
            .addMethod(main)  //添加該類中的方法
            .build(); 

           String packgeName = "com.zs.javapoet" //生成類的包名
    JavaFile file = JavaFile.builder(packgeName, hello).build(); //在控制台輸出
        file.writeTo(System.out);

}
           

輸出結果

`

package com.zs.javapoet;

import java.lang.String;
import java.lang.System;

public class HelloWorld {
  public static void main(String[] args) {
 System.out.println("hello world");
     }
 }
           

`

2.3 具體使用

2.3.1 建立參數 添加多個參數 添加多個方法

`ParameterSpec parameterSpec = ParameterSpec.builder(Integer.class,
            "page", Modifier.FINAL).build();  // 建立參數

    MethodSpec meth1 = MethodSpec.methodBuilder("meth1")
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
            .addParameter(String[].class, "args") // 添加多個參數
            .addParameter(parameterSpec)
            .addStatement("$T.out.println($S)", System.class, "hello world") 
            .build();

    MethodSpec meth2 = MethodSpec
            .methodBuilder("meth2")
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
            .addParameter(Long.class, "price")
            .addStatement("$T.out.println($S)", System.class, "hello world")
            .build();

    TypeSpec class1 = TypeSpec.classBuilder("HelloWorld")
            .addModifiers(Modifier.PUBLIC) 
            .addMethod(meth1) // 在一個類中添加多個方法
            .addMethod(meth2).build();
    JavaFile file = JavaFile.builder("com.zs.javapoet", class1).build(); // 在控制台輸出
    file.writeTo(System.out);
           

`

生成結果

`package com.zs.javapoet;

 import java.lang.Integer;
import java.lang.Long;
import java.lang.String;
import java.lang.System;

public class HelloWorld {
  public static void meth1(String[] args, final Integer page) {
    System.out.println("hello world");
  }

  public static void meth2(Long price) {
    System.out.println("hello world");
  }
}
           

`

2.3.2 if 語句

`       MethodSpec meth2 = MethodSpec
            .methodBuilder("meth2")
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
            .addParameter(Long.class, "price")
            .beginControlFlow(" if (price < 5 ) ")
            .addStatement("price =5 ")
            .endControlFlow()
            .build();

    TypeSpec class1 = TypeSpec.classBuilder("HelloWorld") 
            .addModifiers(Modifier.PUBLIC) 
            .addMethod(meth2).build();
    JavaFile file = JavaFile.builder("com.zs.javapoet", class1).build(); 
    file.writeTo(System.out);`
           

結果為

`package com.zs.javapoet;

    import java.lang.Long;

    public class HelloWorld {
      public static void meth2(Long price) {
         if (price < 5 )  {
          price =5 ;
        }
      }
    }
           

`#### 2.3.2 while 語句 和 $L

$L可以代替常量占據位置

`       int  endCondition =40; // 定義一個常量

    MethodSpec meth2 = MethodSpec
            .methodBuilder("meth2")
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
            .addParameter(Long.class, "endCondition")
            .addStatement("int  i=0")
            .beginControlFlow(" while (price < $L ) ",endCondition) // $L可以代替常量占據位置  beginControlFlow會在該語句後自動加上{
            .addStatement(" i++ ")
            .endControlFlow()
            .build();

    TypeSpec class1 = TypeSpec.classBuilder("HelloWorld") 
            .addModifiers(Modifier.PUBLIC) 
            .addMethod(meth2).build();
    JavaFile file = JavaFile.builder("com.zs.javapoet", class1).build(); 
    file.writeTo(System.out);`
           

結果

`package com.zs.javapoet;

import java.lang.Long;

    public class HelloWorld {
      public static void meth2(Long endCondition) {
        int  i=0;
         while (price < 40 )  {
           i++ ;
        }
      }
    }
           

2.3.3 for 語句 和 $S

$S 可以代替String類型占據位置

`       int  from = 0;
    int to = 10;
    String outPut = "$S代表String";

    MethodSpec meth2 = MethodSpec
            .methodBuilder("meth2")
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
            .beginControlFlow(" for (int i=$L ; i < $L ; i++) ",from,to)
            .addStatement("System.out.println($S)",outPut) //$S 可以代替String類型占據位置
            .endControlFlow()
            .build();


    TypeSpec class1 = TypeSpec.classBuilder("HelloWorld") 
            .addModifiers(Modifier.PUBLIC) 
            .addMethod(meth2).build();
    JavaFile file = JavaFile.builder("com.zs.javapoet", class1).build(); 
    file.writeTo(System.out);
           

結果

`package com.zs.javapoet;

public class HelloWorld {
  public static void meth2() {
     for (int i=0 ; i < 10 ; i++)  {
      System.out.println("$S代表String");
    }
  }
}
           

`

2.3.4 $T

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

結果

`package com.example.helloworld;

    import java.util.Date;

    public final class HelloWorld {
      Date today() {
        return new Date();
      }
    }`
           

2.3.5 $N

在生成的代碼中通常要調用另一個方法,可以使用$N 來代替另一個方法

`       MethodSpec add = MethodSpec.methodBuilder("add")
            .addModifiers(Modifier.PUBLIC).addParameter(Integer.class, "i")
            .returns(Integer.class).addStatement("return i+1").build();

    MethodSpec useAdd = MethodSpec.methodBuilder("usePrint")
            .addModifiers(Modifier.PUBLIC).addStatement("int i=0")
            .addStatement("i=$N(i)", add).build();

    TypeSpec class1 = TypeSpec.classBuilder("HelloWorld")
            .addModifiers(Modifier.PUBLIC).addMethod(add).addMethod(useAdd)
            .build();
    JavaFile file = JavaFile.builder("com.zs.javapoet", class1).build();
    file.writeTo(System.out);
           

`

生成的代碼

`
    package com.zs.javapoet;

    import java.lang.Integer;

    public class HelloWorld {
      public Integer add(Integer i) {
        return i+1;
      }

      public void usePrint() {
        int i=0;
        i=add(i);
      }
    }
           

`

$N 也可代替定義的變量

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

2.3.6 AnnotationSpec

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

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

2.3.8 繼承父類實作接口

接口代碼

package com.zs.javapoet;

public interface TestInterface<T> {
    void test(T testPara);
}
           

父類代碼

package com.zs.javapoet.test;

    public class TestExtendesClass {

    }
           

使用javapoet 生成一個實作TestInterface,并且繼承TestExtendesClass 的類

final ClassName  InterfaceName = ClassName.get("com.zs.javapoet","TestInterface");


    ClassName superinterface = ClassName.bestGuess("com.zs.javapoet.TestClass");
    //ClassName superinterface = ClassName.get("com.zs.javapoet","aa");

    TypeSpec.Builder spec = TypeSpec.classBuilder("TestImpl")
            .addModifiers(Modifier.PUBLIC)
            // 添加接口,ParameterizedTypeName的參數1是接口,參數2是接口的泛型
            .addSuperinterface(ParameterizedTypeName.get(InterfaceName, superinterface)) 
            //使用ClassName.bestGuess會自動導入包
            .superclass(ClassName.bestGuess("com.zs.javapoet.test.TestExtendesClass"));


    MethodSpec.Builder methodSpec = MethodSpec.methodBuilder("test")
            .addAnnotation(Override.class)
            .returns(TypeName.VOID)
            .addParameter(superinterface, "testPara")
            .addStatement("System.out.println(hello)" );

        TypeSpec typeSpec = spec.addMethod(methodSpec.build()).build();

    JavaFile file = JavaFile.builder("com.zs.javapoet", typeSpec).build();
    file.writeTo(System.out);
           

生成的代碼

package com.zs.javapoet;

    import com.zs.javapoet.test.TestExtendesClass;
    import java.lang.Override;

    public class TestImpl extends TestExtendesClass implements TestInterface<TestClass> {
      @Override
      void test(TestClass testPara) {
        System.out.println(hello);
      }
    }
           

3 參考資料

https://github.com/square/javapoet

http://blog.csdn.net/rainbow88888/article/details/51458412?locationNum=3