多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > 綜合技術 > 編譯期注解之JavaPoet

編譯期注解之JavaPoet

來源:程序員人生   發布時間:2017-01-23 20:21:27 閱讀次數:4060次

注解系列

注解基礎

APT

JavaPoet

0x00 概述

上1篇限于篇幅只介紹了APT,這篇來繼續介紹javapoet,是square公司的開源庫。正如其名,java詩人,通過注解來生成java源文件,通常要使用javapoet這個庫與Filer配合使用。主要和注解配適用來干掉那些重復的模板代碼(如butterknife
和databinding所做的事情),固然你也能夠使用這個技術讓你的代碼更加的炫酷。

0x01 簡單使用

使用之前要先引入這個庫

compile 'com.squareup:javapoet:1.7.0'

javapoet是用來生成代碼的,需要借助

經常使用類

使用javapoet前需要了解4個經常使用類

  • MethodSpec 代表1個構造函數或方法聲明。
  • TypeSpec 代表1個類,接口,或枚舉聲明。
  • FieldSpec 代表1個成員變量,1個字段聲明。
  • JavaFile包括1個頂級類的Java文件。

國際慣例先自動生成1個helloWorld類
定義1個編譯期注解

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface clazz_hello {
    String value();
}

然后看下helloworld的注解處理器

@AutoService(Processor.class)
public class HelloWorldProcess extends AbstractProcessor {

    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        for (TypeElement element : annotations) {
            if (element.getQualifiedName().toString().equals(clazz_hello.class.getCanonicalName())) {
                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();

                try {
                    JavaFile javaFile = JavaFile.builder("com.xsf", helloWorld)
                            .addFileComment(" This codes are generated automatically. Do not modify!")
                            .build();
                    javaFile.writeTo(filer);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotations = new LinkedHashSet<>();
        annotations.add(clazz_hello.class.getCanonicalName());
        return annotations;
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }
}

這樣就會在app-build-source-apt-debug-com.xsf文件夾下生成這個文件

0x02 使用進階

方法&控制流:

  • 添加方法 addcodeaddstatement
    對與無需類引入的極簡代碼可以直接使用addCode
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;
  }
}

要是需要import的方法,如上面的.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!") 就需要使用.addStatement來聲明

  • 更優雅的流控制

beginControlFlow 流開啟
addStatement 處理語句
endControlFlow()流結束

如上面的用流改寫就是

MethodSpec main = MethodSpec.methodBuilder("main")
    .addStatement("int total = 0")
    .beginControlFlow("for (int i = 0; i < 10; i++)")
    .addStatement("total += i")
    .endControlFlow()
    .build();

占位符

javapoet里面提供了占位符來幫助我們更好地生成代碼

  • $L 字面常量(Literals)
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();
}

這個就是1個for循環,op負責加減乘除等符號

  • $S 字符串常量(String)
  • $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);

生成的代碼以下,而且會自動導包

package com.example.helloworld;

import java.util.Date;

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

如果我們想要導入自己寫的類怎樣辦?上面的例子是傳入系統的class,這里也提供1種方式,通過ClassName.get(”類的路徑”,”類名“),結合$T可以生成

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

然后生成

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 一樣支持import static,通過addStaticImport

ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");

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();
  • $N 命名(Names),通常指我們自己生成的方法名或變量名等等

比如這樣的代碼塊

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()來代替。

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

構建類的元素

  • Methods

方法的修飾,如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();
}

固然Methods需要和MethodSpec.Builder配置來增加方法參數、異常、javadoc、注解等。

  • 構造器

這個其實也是個函數方法而已,因此可使用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;
  }
}
  • 參數

之前我們是通過addstatement直接設置參數,其實參數也有自己的1個專用類ParameterSpec,我們可使用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();

生成的代碼

void welcomeOverlords(final String android, final String robot) {
}
  • 字段

可使用FieldSpec去聲明字段,然后加到Method中處理

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

通常Builder可以更加詳細的創建字段的內容,比如javadoc、annotations或初始化字段參數等,如:

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;
  • 接口

接口方法必須是PUBLIC ABSTRACT并且接口字段必須是PUBLIC STATIC FINAL ,使用TypeSpec.interfaceBuilder

以下

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

生成的代碼以下

public interface HelloWorld {
  String ONLY_THING_THAT_IS_CONSTANT = "change";

  void beep();
}
  • 枚舉類型

使用TypeSpec.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;
  }
}
  • 匿名內部類

需要使用Type.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();
    }
  });
}

定義匿名內部類的1個特別辣手的問題是參數的構造。在上面的代碼中我們傳遞了不帶參數的空字符串。TypeSpec.anonymousClassBuilder(“”)。

  • 注解

注解使用起來比較簡單

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

生成代碼

  @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⑻")
        .addMember("userAgent", "$S", "Square Cash")
        .build())
    .addParameter(LogRecord.class, "logRecord")
    .returns(LogReceipt.class)
    .build();

代碼生成以下

@Headers(
    accept = "application/json; charset=utf⑻",
    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⑻")
            .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⑻"),
    @Header(name = "User-Agent", value = "Square Cash")
})
LogReceipt recordEvent(LogRecord logRecord);

0x03 后續

在javapoet之前有javawriter,但javapoet有著更強大的代碼模型,并且對類的理解更加到位,因此推薦使用javapoet

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 麻豆亚洲精品一区二区 | 国产精品一区二区三区四区五区 | 欧美亚洲国产精品久久 | 国产91高跟丝袜 | 国产精品久久久久亚洲 | 欧美日韩亚洲国内综合网俺 | 精品欧美一区二区三区四区 | 国产欧美一区二区三区免费看 | 免费观看视频 | 视频一区二区国产无限在线观看 | 国产精品爱久久久久久久三级 | 一二三四在线观看免费播放视频 | 国产日韩在线观看视频 | 中文字幕第6页 | 久久国产免费一区二区三区 | 免费的黄色的网站 | 武则天免费一级淫片 | 中文字幕一区二区三区久久网站 | 校园春色在线视频 | 日韩一级一片 | 在线不卡亚洲 | 日本一区二区三区在线观看视频 | 国产精品一区二区免费 | v亚洲| 在线观看a网站 | 一级淫| 日本免费中文字幕 | 日韩激情中文字幕一区二区 | va亚洲va日韩不卡在线观看 | 伊人网在线视频观看 | 在线视频精品视频 | 国产片一级aaa毛片视频 | 国产高清一区 | 看性过程三级视频在线观看 | 色哟哟www网站入口成人学校 | 中文字幕在线精品 | 成人国产免费 | 欧美老人巨大xxxx做受视频 | 欧美一级毛片高清免费观看 | 成人做视频免费 | 亚洲视频自拍 |