Android areaAndroid知识Android开发

Android中,怎么优雅的生成代码?

2017-04-25  本文已影响721人  街头客

前言

在前面的Android6.0权限处理不再复杂文章中,介绍过一个开源框架PermissionsDispatcher(权限调度者)框架,基于该框架的条件下,有了介绍注解以及注解APT的两篇文章,分别是:

Android进阶之Annotation(注解)的使用

Android编译时,怎么自动生成代码?

【Android编译时,怎么自动生成代码?】这篇文章的结尾中,提到过可以使用JavaPoet来优雅的生成代码。那今天我们的主要内容就是来介绍一下【JavaPoet】该开源框架。

环境的搭建

1,新建项目名为【myJavaPoet】的Android项目;
2,在其中新建Module名为【poet】的模块,选择【Java Library】,如下:

Java Library

PS:

JavaPoet必须在Java Library中使用,例如在rt.jar包中的javax包属于Android核心库中没有,所以不能直接在app Module和Android Library中使用,必须要创建一个Java Library,然后由Java Library导出jar包使用,如下图:

rt.jar

3,在【poet.build.gradle】中,添加如下信息:

dependencies {
    compile 'com.squareup:javapoet:1.8.0'
}

完成后的项目目录结构如下:

项目目录结构

下面开始讲例子,希望看例子的时候,注释切记一定要多注意看~~~

例子1,PoetHello

主要讲的是类、方法的生成,在其中添加修饰、返回类型、抽象方法,以及如何写入文件系统等等。。。

PoetHello的代码如下:

package com.example;


import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.File;

import javax.lang.model.element.Modifier;

/**
 * 使用JavaPoet写一个Hello 2017/4/24 11:01
 */
public class PoetHello {

    /**
     * 生成器 2017/4/24 14:49
     */
    public static void product() {
        try {

            // 创建方法 2017/4/24 14:26
            MethodSpec method = MethodSpec.methodBuilder("hello")
                    .addModifiers(Modifier.PUBLIC) // 修饰符
                    .returns(String.class) // 返回类型
                    .addParameter(String.class, "name", Modifier.FINAL) // 添加final参数 2017/4/25 09:03
                    .addStatement("String say") // 负责分号、换行 2017/4/24 14:52
                    .addStatement("say = $S + name", "Hello ")
                    .addStatement("return say")
                    .build();

            // 抽象方法 2017/4/24 17:03
            MethodSpec abstractMethod = MethodSpec.methodBuilder("abstractHello")
                    .addModifiers(Modifier.ABSTRACT, Modifier.PROTECTED)
                    .build();

            // 创建类
            TypeSpec hello = TypeSpec.classBuilder("Hello")
                    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                    .addMethod(method) // 添加方法
                    .addMethod(abstractMethod)
                    .build();

            // 写入文件系统 2017/4/24 14:27
            JavaFile javaFile = JavaFile.builder("com.example.all_product", hello)
                    .build();

            javaFile.writeTo(new File("poet/src/main/java")); // 指定文件路径
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        PoetHello.product();
    }

}

生成的Hello.java类,代码如下:

package com.example.all_product;

import java.lang.String;

public abstract class Hello {
  public String hello(String name) {
    String say;
    say = "Hello " + name;
    return say;
  }

  protected abstract void abstractHello();
}

例子2,PoetFor

主要讲的就是beginControlFlow() 、endControlFlow()的配合使用,提供换行符与缩进。以for循环为例子,其它使用方法类似,如if、while、switch等等。。。

其中还有$S、$T、$N、$L的使用,意思分别如下:

$S for Strings

顾名思义,$S代表的是String,将其看成是一个字符创即可。

$T for Types

这里的Type/类型,其实可以简单的理解成我们的Class类即可。

$N for Names

可以理解成引用另一个变量名即可。

$L for Literals

这个刚开始其实是比较不好理解,但场景使用多了就应该慢慢能理解了,在这里其实就可以简单的理解成【Formatter】即可。

PoetFor代码如下(切记看注释):

package com.example;

import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.File;

import javax.lang.model.element.Modifier;

/**
 * 生成for代码 2017/4/24 14:57
 */
public class PoetFor {

    /**
     * for循环生成器 2017/4/24 14:57
     */
    public static void product(int from, int to) {
        try {

            // 创建全局字段,并初始化 2017/4/25 09:15
            FieldSpec fieldSpec = FieldSpec.builder(String.class, "DESC")
                    .addModifiers(Modifier.PRIVATE, Modifier.STATIC, Modifier.FINAL)
                    .initializer("$S", "Sum is ")
                    .build();

            // 生成方法 2017/4/24 15:09
            MethodSpec methodSpec = MethodSpec.methodBuilder("forInteger")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(void.class) // 无返回值
                    .addStatement("int sum = 0")

                    // beginControlFlow() + endControlFlow() 需要一起使用,提供换行符和缩进。 2017/4/24 15:09
                    // $L 占位符,类似于Formatter,替换int类型 2017/4/24 15:25
                    .beginControlFlow("for (int i = $L; i <= $L; i++)", from, to)
                    .addStatement("sum += i")
                    .endControlFlow()

                    // $T 替换类名   $S 替换字符串 2017/4/24 15:31
                    // 使用System会自动import使用 2017/4/24 15:34
                    .addStatement("$T.out.println($N + sum)", System.class, "DESC")
                    .build();

            // 类生成 2017/4/24 15:10
            TypeSpec typeSpec = TypeSpec.classBuilder("ForInteger")
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(methodSpec)
                    .addField(fieldSpec)
                    .build();

            // 文件生成 2017/4/24 15:10
            JavaFile javaFile = JavaFile.builder("com.example.all_product", typeSpec)
                    .addFileComment("JavaPoet生成For循环代码 2017/4/24 15:07")
                    .build();

            javaFile.writeTo(new File("poet/src/main/java"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        PoetFor.product(0, 10);
    }

}

生成的ForInteger.java类如下:

// JavaPoet生成For循环代码 2017/4/24 15:07
package com.example.all_product;

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

public class ForInteger {
  private static final String DESC = "Sum is ";

  public static void forInteger() {
    int sum = 0;
    for (int i = 0; i <= 10; i++) {
      sum += i;
    }
    System.out.println(DESC + sum);
  }
}

例子3,PoetList

主要讲的是ClassName的硬指定,以及引用指定;然后再讲了使用ParameterizedTypeName.get(ClassName, ClassName)创建List<Object>泛型的使用。

PoetList的代码如下:

package com.example;

import com.example.all_product.ForInteger;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.File;

import javax.lang.model.element.Modifier;

/**
 * 使用ClassName(重要),生成List代码 2017/4/24 16:04
 */
public class PoetList {

    /**
     * 生成器 2017/4/24 16:04
     */
    public static void product() {
        try {

            // list泛型的类型 2017/4/24 16:06
            ClassName type = ClassName.get("com.example.all_product", "ForInteger");

            // List类型
            ClassName list = ClassName.get("java.util", "List");

            // ArrayList类型
            ClassName arrayList = ClassName.get("java.util", "ArrayList");

            // 生成泛型类型,类型的名称、参数的名称 2017/4/24 16:08
            TypeName listType = ParameterizedTypeName.get(list, type);

            MethodSpec methodSpec = MethodSpec.methodBuilder("listType")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(listType)
                    .addStatement("$T lstHello = new $T<>()", listType, arrayList)

                    // ClassName类的绝对路径,内部利用该路径进行反射获取实体类,使用场景是当该类也是生成类时,可以硬指定 2017/4/24 16:18
                    .addStatement("lstHello.add(new $T())", type) // ClassName指定
                    .addStatement("lstHello.add(new $T())", ForInteger.class) // 引用指定
                    .addStatement("return lstHello")
                    .build();

            TypeSpec typeSpec = TypeSpec.classBuilder("ListType")
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(methodSpec)
                    .build();

            JavaFile javaFile = JavaFile.builder("com.example.all_product", typeSpec)
                    .build();

            javaFile.writeTo(new File("poet/src/main/java"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        PoetList.product();
    }

}

生成的ListType.java类,代码如下:

package com.example.all_product;

import java.util.ArrayList;
import java.util.List;

public class ListType {
  public static List<ForInteger> listType() {
    List<ForInteger> lstHello = new ArrayList<>();
    lstHello.add(new ForInteger());
    lstHello.add(new ForInteger());
    return lstHello;
  }
}

例子4,PoetMap

上面例子3讲的是List<Object>的使用,那么下面再来讲一下Map<Key, Value>的创建使用。

PoetMap的代码如下:

package com.example;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.File;

import javax.lang.model.element.Modifier;

/**
 * 使用ClassName(重要),生成Map代码 2017/4/25 13:39
 */
public class PoetMap {

    /**
     * 生成器 2017/4/25 13:39
     */
    public static void product() {
        try {

            // list泛型的类型 2017/4/24 16:06
            ClassName type = ClassName.get("com.example.all_product", "ForInteger");

            // key 2017/4/25 13:38
            ClassName string = ClassName.get("java.land", "String");

            // map类型
            ClassName map = ClassName.get("java.util", "Map");

            // HashMap类型
            ClassName hashMap = ClassName.get("java.util", "HashMap");

            // 生成Map类型,类型的名称、Key、Value 2017/4/25 14:08
            TypeName listType = ParameterizedTypeName.get(map, string, type);

            MethodSpec methodSpec = MethodSpec.methodBuilder("listType")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                    .returns(listType)
                    .addStatement("$T mapHello = new $T<>()", listType, hashMap)
                    .addStatement("mapHello.put($S, new $T())", "key", type)
                    .addStatement("return mapHello")
                    .build();

            TypeSpec typeSpec = TypeSpec.classBuilder("MapType")
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(methodSpec)
                    .build();

            JavaFile javaFile = JavaFile.builder("com.example.all_product", typeSpec)
                    .build();

            javaFile.writeTo(new File("poet/src/main/java"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        PoetMap.product();
    }

}

生成的MapType.java,代码如下:

package com.example.all_product;

import java.util.HashMap;
import java.util.Map;

public class MapType {
  public static Map<String, ForInteger> listType() {
    Map<String, ForInteger> mapHello = new HashMap<>();
    mapHello.put("key", new ForInteger());
    return mapHello;
  }
}

例子5,PoetInterface

该例子讲的是利用JavaPoet来生成接口,使用的是TypeSpec.interfaceBuilder(InterfaceName)方法来创建接口类,也可以使用TypeSpec中的其它方法来创建枚举、注解等等。。。

PoetInterface的代码如下:

package com.example;

import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.File;

import javax.lang.model.element.Modifier;

/**
 * 生成接口 2017/4/25 09:16
 */
public class PoetInterface {

    /**
     * 生成器 2017/4/25 09:40
     */
    public static void product() {

        try {

            // 生成方法,必须有Modifier.ABSTRACT或STATIC 2017/4/25 09:25
            MethodSpec methodSpec = MethodSpec.methodBuilder("getName")
                    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                    .addJavadoc("获取名称\n") // 添加注释 2017/4/25 11:24
                    .returns(String.class)
                    .build();

            // 生成字段,必须同时有Modifier.STATIC, Modifier.FINAL 2017/4/25 09:31
            FieldSpec fieldSpec = FieldSpec.builder(String.class, "name")
                    .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
                    .initializer("$S", "CCB")
                    .build();

            // interfaceBuilder方法标志生成接口类 2017/4/25 09:25
            TypeSpec typeSpec = TypeSpec.interfaceBuilder("UserInterface")
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(methodSpec)
                    .addField(fieldSpec)
                    .build();

            // 写入文件
            JavaFile javaFile = JavaFile.builder("com.example.all_product", typeSpec)
                    .addFileComment("Poet生成接口 2017/4/25 09:24")
                    .build();

            javaFile.writeTo(new File("poet/src/main/java"));
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static void main(String[] args) {
        PoetInterface.product();
    }

}

生成的UserInterface.java接口类,代码如下:

// Poet生成接口 2017/4/25 09:24
package com.example.all_product;

import java.lang.String;

public interface UserInterface {
  String name = "CCB";

  /**
   * 获取名称
   */
  String getName();
}

例子6,PoetSort

主要讲的就是怎么创建匿名类。该例子先自动创建一个实体,再引用该实体类做sort排序。

PoetSort的代码如下:

package com.example;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.File;
import java.util.Collections;

import javax.lang.model.element.Modifier;

/**
 * 写匿名类进行排序 2017/4/25 09:39
 */
public class PoetSort {

    public static void product() {
        try {

            // 生成实体 2017/4/25 09:41
            TypeSpec typeEntity = TypeSpec.classBuilder("UserEntity")
                    .addModifiers(Modifier.PUBLIC)
                    .addField(FieldSpec.builder(String.class, "name")
                            .addModifiers(Modifier.PRIVATE)
                            .build())
                    .addMethod(MethodSpec.methodBuilder("getName")
                            .addModifiers(Modifier.PUBLIC)
                            .returns(String.class)
                            .addStatement("return this.$N", "name")
                            .build())
                    .addMethod(MethodSpec.methodBuilder("setName")
                            .addModifiers(Modifier.PUBLIC)
                            .addParameter(String.class, "name")
                            .returns(void.class)
                            .addStatement("this.$N = $N", "name", "name")
                            .build())
                    .addField(FieldSpec.builder(int.class, "time")
                            .addModifiers(Modifier.PRIVATE)
                            .build())
                    .addMethod(MethodSpec.methodBuilder("getTime")
                            .addModifiers(Modifier.PUBLIC)
                            .returns(int.class)
                            .addStatement("return this.$N", "time")
                            .build())
                    .build();

            // 写入用户实体 2017/4/25 09:48
            JavaFile javaUserEntity = JavaFile.builder("com.example.all_product", typeEntity)
                    .build();

            javaUserEntity.writeTo(new File("poet/src/main/java"));

            // 生成排序匿名类 2017/4/25 09:49
            ClassName classComparator = ClassName.get("java.util", "Comparator"); // 指向Comparator
            ClassName classList = ClassName.get("java.util", "List"); // 指向List
            ClassName classUser = ClassName.get("com.example.all_product", "UserEntity"); // 指向User实体

            // 匿名比对类 2017/4/25 09:52
            TypeSpec typeCompare = TypeSpec.anonymousClassBuilder("")
                    .addSuperinterface(ParameterizedTypeName.get(classComparator, classUser))
                    .addMethod(MethodSpec.methodBuilder("compare")
                            .addAnnotation(Override.class) // 添加注解 2017/4/25 10:56
                            .addModifiers(Modifier.PUBLIC)
                            .addParameter(classUser, "obj1")
                            .addParameter(classUser, "obj2")
                            .returns(int.class)
                            .beginControlFlow("if ($N.$N() > $N.$N())",
                                    "obj1", "getTime", "obj2", "getTime")
                            .addStatement("return 1")
                            .endControlFlow()
                            .beginControlFlow("else if ($N.$N() < $N.$N())",
                                    "obj1", "getTime", "obj2", "getTime")
                            .addStatement("return -1")
                            .endControlFlow()
                            .beginControlFlow("else")
                            .addStatement("return 0")
                            .endControlFlow()
                            .build())
                    .build();

            // 外部类 2017/4/25 09:59
            TypeSpec typeSort = TypeSpec.classBuilder("UserSort")
                    .addModifiers(Modifier.PUBLIC)
                    .addMethod(MethodSpec.methodBuilder("sort")
                            .addModifiers(Modifier.PUBLIC)
                            .returns(void.class)
                            .addParameter(ParameterizedTypeName.get(classList, classUser), "lstUsers")
                            .addStatement("$T.sort($N, $L)", Collections.class, "lstUsers", typeCompare)
                            .build())
                    .build();

            // 写入排序类 2017/4/25 10:03
            JavaFile javaUserSort = JavaFile.builder("com.example.all_product", typeSort)
                    .build();

            javaUserSort.writeTo(new File("poet/src/main/java"));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        PoetSort.product();
    }

}

生成的UserEntity.java和UserSort.java类的代码如下:

package com.example.all_product;

import java.lang.String;

public class UserEntity {
  private String name;

  private int time;

  public String getName() {
    return this.name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getTime() {
    return this.time;
  }
}

package com.example.all_product;

import java.lang.Override;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class UserSort {
  public void sort(List<UserEntity> lstUsers) {
    Collections.sort(lstUsers, new Comparator<UserEntity>() {
      @Override
      public int compare(UserEntity obj1, UserEntity obj2) {
        if (obj1.getTime() > obj2.getTime()) {
          return 1;
        }
        else if (obj1.getTime() < obj2.getTime()) {
          return -1;
        }
        else {
          return 0;
        }
      }
    });
  }
}

例子7,PoetExtend

主要就讲一下怎么使用superclass(ClassName)的方法来创建继承的子类,同理,也可以使用addSuperinterface(ClassName)的方法来创建实现接口,实现接口后,怎么重写接口的方法,可参考例子5。。。

PoetExtend的代码如下:

package com.example;

import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.File;

import javax.lang.model.element.Modifier;

/**
 * 生成继承的子类 2017/4/25 14:48
 */
public class PoetExtend {

    public static void product() {
        try {

            // 生成实体 2017/4/25 14:48
            TypeSpec typeEntity = TypeSpec.classBuilder("SubUserEntity")
                    .addModifiers(Modifier.PUBLIC)

                    // 继承UserEntity类 2017/4/25 14:58
                    .superclass(ClassName.get("com.example.all_product", "UserEntity"))

                    // 实现UserInterface接口
                    .addSuperinterface(ClassName.get("com.example.all_product", "UserInterface"))
                    .addField(FieldSpec.builder(int.class, "id")
                            .addModifiers(Modifier.PRIVATE)
                            .build())
                    .addMethod(MethodSpec.methodBuilder("getId")
                            .addModifiers(Modifier.PUBLIC)
                            .returns(int.class)
                            .addStatement("return this.$N", "id")
                            .build())
                    .build();

            // 写入用户实体 2017/4/25 14:55
            JavaFile javaUserEntity = JavaFile.builder("com.example.all_product", typeEntity)
                    .build();

            javaUserEntity.writeTo(new File("poet/src/main/java"));

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        PoetExtend.product();
    }

}

生成的SubUserEntity代码如下:

package com.example.all_product;

public class SubUserEntity extends UserEntity implements UserInterface {
  private int id;

  public int getId() {
    return this.id;
  }
}

总结

怎么动态的生成java代码,我们今天主要讲7个例子就好了,在我们写项目时,如果需要动态生成代码时,这应该足够应付80%的开发工作了,如果需要深入创建更复杂的代码,请移步参考JavaPoet开源框架。

结合前面几篇文章,就已经包含了创建注解、注解解析、代码生成,慢慢。。。慢慢。。。我们向自己写框架靠近。。。其实框架中经常使用的还有另一个检查工具,lint。我们可以自定义lint的检查规则,不过,我现在有一个地方卡住了,还没解决,后面如果解决了,有机会就一并讲一下。

今天就讲到这里了。。。

谢谢支持~~~

上一篇下一篇

猜你喜欢

热点阅读