使用CodeModel生成Java类

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方法获得,用于向方法中添加代码
JArrayJava数组类型
JExpressionJava表达式类型
JExprJava表达式工具类,用于生成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吧各位,这篇文章只是历史遗留物品而已)


本篇文章仅代表个人观点,难免会有疏漏或者谬误之处,欢迎批评指正。

参考资料

  1. 官方API文档(已停止维护)
  2. 中文参考手册
  3. 英文简要教程

转载于:https://juejin.im/post/5a3324d551882549a5424429

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值