Android中,怎么优雅的生成代码?
前言
在前面的Android6.0权限处理不再复杂文章中,介绍过一个开源框架PermissionsDispatcher(权限调度者)框架,基于该框架的条件下,有了介绍注解以及注解APT的两篇文章,分别是:
Android进阶之Annotation(注解)的使用
Android编译时,怎么自动生成代码?
在【Android编译时,怎么自动生成代码?】这篇文章的结尾中,提到过可以使用JavaPoet来优雅的生成代码。那今天我们的主要内容就是来介绍一下【JavaPoet】该开源框架。
环境的搭建
1,新建项目名为【myJavaPoet】的Android项目;
2,在其中新建Module名为【poet】的模块,选择【Java Library】,如下:
PS:
JavaPoet必须在Java Library中使用,例如在rt.jar包中的javax包属于Android核心库中没有,所以不能直接在app Module和Android Library中使用,必须要创建一个Java Library,然后由Java Library导出jar包使用,如下图:
rt.jar3,在【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的检查规则,不过,我现在有一个地方卡住了,还没解决,后面如果解决了,有机会就一并讲一下。
今天就讲到这里了。。。
谢谢支持~~~