Java进阶学习

什么是元编程?

2018-11-28  本文已影响0人  Java大宝宝

新任务看起来非常简单:从一个CSV文件中读取数据,形成Java对象,然后对外提供一个API,让别人调用。

这个CSV文件叫做employee.csv, 张大胖打开这个CSV文件,里边的内容一看就懂。

name,age,level

Andy,25,B7

Joe, 22, B6

张大胖的API就需要返回一个List<Employee>,很自然,Employee类长这个样子:

publicclassEmployee{

privateStringname;

privateStringage;

privateStringlevel;

......

}

class中的每个字段和csv文件的“表头”的“列名”保持一致。

这样简单的任务对张大胖来说是小菜一碟,他写了一个EmployeeParser,专门解析CSV文件,形成Employee对象,半个小时不到就收工了,赶紧下班!

还没来得及溜走,又被领导叫住了:“大胖,那个CSV文件新加了一个字段,叫做salary ,快把你的程序改一下啊!”

name,age,level,salary

Andy,25,B7,3000

Joe, 22, B6,2500

张大胖极不情愿地坐下来,给Employee类增加了一个salary的字段,又修改了EmployeeParser类,增加对这个字段的解析。

然后又听到领导在喊:“又加了一个字段,叫做tax !”

没辙,继续修改Employee类和EmployeeParser类吧。 这一次修改完,领导终于放他走了。

模板:用程序来生成程序

等了两趟车,终于在西二旗挤上了13号线,张大胖心里一直在想:明天保不齐还要增加字段,这真是让人厌烦的重复劳动啊。大家都说,Don't repeat yourself, 我这怎么才能减少重复呢?

关键点就在于,那个Java类的字段要和CSV的表头的列名做对应,CSV变化了,Java类的字段以及解析的方法都要做相应得修改才可以。

对了,能不能根据CSV的列名自动地生成那个Employee类啊,这样问题不就解决了吗? CSV变化, Employee类跟着变化,多好!

CSV的“列名”经过读取,可以变成一个Java 的List ,例如["name","age","level"], 如何写一段代码,把这个List变成一个Employee Class呢?

张大胖聚精会神,在地铁上想了一路,完全无视地铁上那拥挤的人群和污浊的空气。

快要下车时,他灵机一动,可以用模板技术嘛,比如velocity模板,定义一个employee.vm :

publicclassEmployee{

#foreach ($field in $headers)

privateString $field;

#end    

##其他代码略

}

然后再写一个代码生成器,读取employee.csv的“表头”,形成List,把List传递给这个employee.vm模板,就可以输出Java类了:

写成具体的代码就是这个样子:

VelocityEngine ve =newVelocityEngine();

...初始化引擎的代码略...

Templatetemplate= ve.getTemplate("employee.vm");

VelocityContext context =newVelocityContext();

List headers = readCSVHeaders();

context.put("headers",headers);

Writer writer =newPrintWriter(newFileOutputStream(

newFile("C:\\Employee.java")));

//把headers变量传递给模板

template.merge(context, writer);

writer.flush();

(友情提示:可左右滑动)

(码农翻身注:这里做了简化只关注了Empployee的字段,还需要处理getter/setter方法,尤其是也需要通过模板的方法生成EmployeeParser,用来形成Employee对象。此外还有数据类型的问题。)

在小区对面的田老师红烧肉吃了一份盖饭以后,张大胖立刻投入到程序的编写中来,一边写一边想:我这是用程序来生成程序啊!

元编程

第二天,领导果然要加新的字段了,张大胖心中暗自佩服自己的自知之明,调出昨晚写的“宝贝”执行了一下,不到一秒钟,新的Employee和EmployeeParser就生成了。

下午的时候,张大胖洋洋得意地给Bill展示自己的工作成果,Bill说:“不错啊,都开始元编程了!”

“元编程?”

“对啊,你不是用程序来生成程序嘛,这就是一种元编程。”

张大胖没想到的工作居然就是高大上的“元编程”,更高兴了。

“还有,如果把CSV文件看成数据库的表,代码生成器自动生成的EmployeeParser不就相当于DAO吗?Employeeb 不就是和数据表映射的Domain对象吗? 你的代码实现了Object-relational mapping !”

就是啊,我怎么没想到,虽然距离真正的O/R Mapping还很远,但思想是一致的,大神就是厉害,看透了本质,张大胖暗想。

可是Bill很快给它泼了一盆冷水:“不过这种用模板生成的方式还是有些‘低级’,每次CSV文件有变化,都需要运行一下代码生成器才可以。”

“那怎么办?”

“其实吧,这个Employee的类没有必要在编译期存在,如果能在运行时动态地生成就行了。”

运行期动态生成? 张大胖有点懵。

“对于Java语言来说,运行期在内存中动态生成一个Class,还是有难度的,你需要透彻理解Java Class的文件格式,还需要在底层需要用ASM这样的东西去操作Java字节码。”

“文件格式和字节码?就是那些0xCAFEBABE,iload ,iadd, putfield,invokespecial ? ”  张大胖看过虚拟机的书,知道有很多字节码,但是操作它们形成符合要求的类,实在是难以想象。

Bill 笑道:“你可以用动态语言,比如Ruby,元编程很强大,实现你这个功能简直是小菜一碟。”

Bill很快就写出了一段代码:

#在内存中创建一个名称为Employee的类

klass = Object.const_set("Employee", Class.new)

names= ...读取csv文件第一行,形成数组,如 ["name","age","level"]...

#对这个内存中的类进行"手术"

klass.class_evaldo

#现在 name,age,level...变成了这个Employee类的字段!

attr_accessor*names

#再定义一个Employee类的构造函数    

define_method(:initialize)do|*values|

names.each_with_indexdo|name, i|

instance_variable_set("@"+ name, values[i])

end

end

end

(友情提示:可左右滑动)

张大胖没有学过Ruby , 看到这里更懵了。

Bill看到张大胖发呆的样子,说道:”经过上述处理,内存中创建了一个类,如果把它的源码展示一下,你就明白了。”

#动态生成的类

classEmployee

#动态生成的属性,类似与java的getter方法

defname

@name

end

#动态生成的属性,类似java的setter方法

defname=(str)

@name = str

end

defage

@age

end

defage=(str)

@age = str

end

deflevel

@level

end

deflevel=(str)

@level = str

end

#动态生成的构造函数

definitialize(*values)

@name = values[0]

@age = values[1]

@level = values[2]

end

end

#一个使用Employee类的例子

p = Employee.new("andy","22","B6")

(友情提示:可左右滑动)

(码农翻身注:对CSV文件内容的读取没有包括在其中。)

张大胖明白了,这个类是由数据驱动,动态生成的,CSV的header 中有多少字段,这个类就会生成多少个属性。

和自己的代码生成器比较了一下,Ruby写的这段代码更加精炼,不需要模板,没有所谓代码生成器,或者说,代码生成器和生成的类已经合二为一了。

即使是CSV文件发生了变化,也不需要额外运行代码生成器,只需要执行那段Ruby代码就行。

Bill问道:“怎么样,元编程不错吧?”

张大胖说道:“嗯, 这Ruby的元编程能力很强大啊,可惜的是,我们的项目都是Java的,这动态的脚本语言Ruby没法直接使用,如果是微服务,对外提供的是HTTP的API,我可以学学Ruby,单独写个Ruby项目。”

Bill说:“其实吧,编程语言中,元编程能力最强大的还属LISP,在LISP当中,程序和数据的表现形式是一致的,造就了它无以伦比的元编程能力,LISP程序可以像操作数据一样操作代码。 有人甚至说,LISP根本不是编程语言,它是编程元语言,专门为了生成程序而生。”

张大胖听得云里雾里,黯然道:“不知道你在说什么,太抽象了!等我学学LISP以后再回来和你讨论吧。”

如果您觉得不错,请别忘了转发、分享、点赞让更多的人去学习, 您的举手之劳,就是最好的支持,非常感谢!

在此我向大家推荐一个架构学习交流群。交流学习群号:938837867 暗号:555 里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构等这些成为架构师必备

上一篇 下一篇

猜你喜欢

热点阅读