CodeModel
CodeModel是什么
CodeModel是一个Java库,使用它能通过Java代码生成Java类。乍看之下,除了“不明觉厉”,想不出来还能用什么词来形容他,说得不好听一点,有什么用呢?
怎么用
说完了是什么,接下来就是怎么用。本篇文章只需要讲解基本用法,所以IDE就使用Intellij Idea 2017 社区版。 为了方便引用库,所以新建一个Gradle的Java项目,然后在项目的build.gradle文件中添加依赖:
compile 'com.sun.codemodel:codemodel:2.6'
复制代码
操作方法跟Android Studio里的差不多。
开始吧
如何生成一个类
很简单,看代码:
public class Main {
public static void main(String[] args) throws JClassAlreadyExistsException, IOException {
//实例化JCodeModel对象
JCodeModel jCodeModel = new JCodeModel();
//生成package
JPackage jPackage = jCodeModel._package("com.peceoqicka.cm");
//在指定package下生成类
jPackage._class("MyClass");
//将生成的类代码写入文件
jCodeModel.build(new File("src/main/java/"));
}
}
复制代码
API中常用的类
类名 | 说明 |
JPackage | 包类型,可由JCodeModel#_package方法得到,主要作用是调用_class方法在指定包里生成类 |
JDefinedClass | 定义类类型,提供各种方法如向定义类里添加方法或者成员等 |
JClass | 已有类类型,通常为引用已经有的类如String |
JFieldVar | 成员变量类型 |
JMethod | 方法类型 |
JBlock | 代码块类型,通常由JMethod#_body方法获得,用于向方法中添加代码 |
JArray | Java数组类型 |
JExpression | Java表达式类型 |
JExpr | Java表达式工具类,用于生成JExpression |
Jvar | 局部变量类型 |
成员变量与局部变量
成员变量
成员变量的定义:
JFieldVar fieldVar = jDefinedClass.field(JMod.PRIVATE+JMod.STATIC+JMod.FINAL,
String.class, "CONSTANT_STR_NAME", JExp.lit("Gradle"));
复制代码
代码运行的结果是:
public class MyClass {
public final static String CONSTANT_STR_NAME = "Gradle";
}
复制代码
很好理解,一共4个参数,第一个参数定义变量的修饰符,凡是你能想到的例如public、static、private等等都是引用的JMod这个类里边的常量;第二个参数定义变量的类型,可以直接传入已有的类型的class,或者引用用JCodeModel生成的类型(JType);第三个参数定义变量的名称;第四个参数是可选的,如果你需要在定义的时候初始化这个变量,那就传入第四个参数,否则就不传。
field方法一共有4个重构方法:
field(int mods, Class<?> type, String name)
field(int mods, JType type, String name)
field(int mods, Class<?> type, String name, JExpression init)
field(int mods, JType type, String name, JExpression init)
复制代码
其中JExpression是表达式类,可以通过JExpr工具类来获得,直接赋值可以使用:
JExpr.lit(String n)
JExpr.lit(int n)
...
复制代码
String以及所有基本值类型都可以通过这种方法来赋值。通过等号赋值请参看方法部分的说明。
局部变量
局部变量的定义:
JVar jVar = methodBody.decl(jCodeModel.INT, "i");
methodBody.assign(jVar, JExpr.lit(6));
复制代码
代码运行的结果:
int i;
i = 6;
复制代码
decl方法的重构方法:
decl(JType type, String name)
decl(JType type, String name, JExpression init)
decl(int mods, JType type, String name, JExpression init)//用于定义final局部变量
复制代码
那么如果局部变量的赋值是一个方法调用的结果怎么办,别急:
methodBody.assign(jVar, JExpr.invoke(jVarClass, "methodAnother")
.arg(JExpr.lit(5));
复制代码
这行代码对应的生成代码为:
i = someClass.methodAnother(5);
复制代码
可以很明显的看出来,方法调用用JExpression来表达就是JExpr.invoke,第一个参数是指明调用的方法是属于哪个类的实例的,第二个参数指明方法名称。如果调用的方法要传入参数,那么直接在invoke后方调用arg方法传入,有多少个参数就调用多少次arg方法,都是链式调用。
方法
方法的定义:
JMethod jMethodGWC = jDefinedClass.method(JMod.PUBLIC, jCodeModel.INT, "getWheelCount");
JBlock jBlockGWC = jMethodGWC.body();
JVar jVarC = jBlockGWC.decl(jCodeModel.INT, "count");
jBlockGWC.assign(jVarC, JExpr.lit(6));
jBlockGWC._return(jVarC);
复制代码
对应的代码:
public int getWheelCount() {
int count;
count = 6;
return count;
}
复制代码
为方法添加参数:
jMethod.param(jCodeModel.INT, "type");//int type
复制代码
传入参数与定义局部变量的decl方法的一致。JBlock的_return方法定义了方法体的返回语句。
构造函数
构造函数就是没有返回值的方法,定义:
JMethod constructor = jDefinedClass.constructor(JMod.PUBLIC);
//public Car(){}
复制代码
那么添加参数和在方法体中添加代码的方法和普通方法完全一样。
类和接口
类的定义:
JDefinedClass jDefinedClass = jPackage._class("Car");
//public class Car{}
复制代码
默认为public,定义抽象类直接在参数里添加JMod.ABSTRACT。
JDefinedClass jDefinedClass = jPackage._class(JMod.ABSTRACT, "Car");
//public abstract class Car{}
复制代码
定义接口:
JDefinedClass jDefinedClass = jPackage._class(JMod.ABSTRACT, "ICar", ClassType.INTERFACE);
//public interface ICar{}
复制代码
继承和实现(接口):
jDefinedClass._extends(MyClass.class);
jDefinedClass._implements(MyInterface.class);
复制代码
引用已有的类型:
JClass jClass = jCodeModel.ref(String.class);
jClass = jCodeModel.ref("java.lang.String");
复制代码
条件语句
if...else
定义if条件语句:
JConditional jConditionalGGN = jBlockGGN._if(JExpr.ref("sex").eq(JExpr.lit(1)));
jConditionalGGN._then().block()._return(JExpr.lit("Female"));
jConditionalGGN._else().block()._return(JExpr.lit("Male"));
复制代码
对应的代码:
if (sex == 1) {
return "Female";
} else {
return "Male";
}
复制代码
block方法返回的同样是JBlock类型。
switch
定义switch语句:
JSwitch jSwitchGGNS = jBlockGGNS._switch(JExpr.ref("sex"));
jSwitchGGNS._case(JExpr.lit(1)).body()._return(JExpr.lit("Female"));
jSwitchGGNS._case(JExpr.lit(0));
jSwitchGGNS._default().body()._return(JExpr.lit("Male"));
复制代码
对应的代码:
public static String getGenderName(int sex) {
switch (sex) {
case 1:
return "Female";
case 0:
default:
return "Male";
}
}
复制代码
循环语句
for循环
定义for循环:
ForLoop jForLoop = jBlockDS._for();
jForLoop.init(jCodeModel.INT, "i", JExpr.lit(0));//初始化部分
jForLoop.test(JExpr.ref("i").lt(JExpr.lit(100)));//条件判断部分
jForLoop.update(JExpr.ref("i").incr());//条件变化部分
JBlock jBlockFLDS = jForLoop.body();//获得for循环代码块
JClass jClassSystem = jCodeModel.ref(System.class);
JFieldRef jFieldRefSOut = jClassSystem.staticRef("out");
//for循环代码块中调用System.out.println
jBlockFLDS.invoke(jFieldRefSOut, "println").arg("hello");
复制代码
对应的代码:
for (int i = 0; (i< 100); i ++) {
System.out.println("hello");
}
复制代码
foreach循环
定义foreach循环:
//定义一个字符串数组,并添加初始化数据
JArray jArray = JExpr.newArray(jCodeModel.ref(String.class));
jArray.add(JExpr.lit("Amy"));
jArray.add(JExpr.lit("Bruce"));
jArray.add(JExpr.lit("Cherry"));
jArray.add(JExpr.lit("Douglas"));
jArray.add(JExpr.lit("Ella"));
jArray.add(JExpr.lit("Fin"));
//定义定义局部变量引用刚才的数组
JVar arr = jBlockDS.decl(jCodeModel.ref(String[].class), "arr", jArray);
JForEach jForEach = jBlockDS.forEach(jCodeModel.ref(String.class), "str", arr);
JFieldRef jFieldRefSOut = jCodeModel.ref(System.class).staticRef("out");
jForEach.body().invoke(jFieldRefSOut, "println").arg(jForEach.var());
复制代码
对应的代码:
String[] arr = new String[] {"Amy", "Bruce", "Cherry", "Douglas", "Ella", "Fin"};
for (String str: arr) {
System.out.println(str);
}
复制代码
while循环
定义while循环:
//定义局部变量i=0
JVar jVarI = jBlockDS.decl(jCodeModel.INT, "i", JExpr.lit(0));
//生成while语句,添加条件i<10
JWhileLoop jWhileLoop = jBlockDS._while(JExpr.ref("i").lt(JExpr.lit(10)));
//同上调用方法System.out.println,参数为i
JFieldRef jFieldRefSOut = jCodeModel.ref(System.class).staticRef("out");
jWhileLoop.body().invoke(jFieldRefSOut, "println").arg(jVarI);
//附加语句i+=1
jWhileLoop.body().assignPlus(jVarI, JExpr.lit(1));
复制代码
对应的代码:
int i = 0;
while (i< 10) {
System.out.println(i);
i += 1;
}
复制代码
do...while循环
定义do..while循环的代码与while的代码几乎完全一致,将第一行改为调用_do:
JVar jVarI = jBlockDS.decl(jCodeModel.INT, "i", JExpr.lit(0));
//定义do..while循环,同样传入条件,其余代码几乎完全一样
JDoLoop jDoLoop = jBlockDS._do(JExpr.ref("i").lt(JExpr.lit(10)));
JFieldRef jFieldRefSOut = jCodeModel.ref(System.class).staticRef("out"); jDoLoop.body().invoke(jFieldRefSOut, "println").arg(jVarI);
jDoLoop.body().assignPlus(jVarI, JExpr.lit(1));
复制代码
对应的代码:
int i = 0;
do {
System.out.println(i);
i += 1;
} while (i< 10);
复制代码
Emmm...
这篇文章作为CodeModel库的简要用法参考手册,是我在学习了仅有的少量资料的情况下自行总结的。最近发现官方文档的页面不知什么原因不再维护了,恐怕以后会删除这个项目,不过库本身还能在maven仓库中找到,还能够继续使用,只是参考资料不会再有更新了。当然这个库设计的思路还是很好的,可以作为一个范本,进而学习它的基本原理,然后自己实现这样一个库,岂不美哉。(其实这个库已经落后了,还是用Freemarker吧各位,这篇文章只是历史遗留物品而已)