Groovy元编程(1)

Groovy

使用ruby也好久了,虽然钟情于ruby语法的无穷灵活性,不过一直苦于没法使用java平台上各种各样的框架。虽然也早有jruby这样的存在,但是速度还是很成问题,而且这种间接的调用,使用起来也感觉不舒服。

学习了groovy之后,我觉得,如果喜欢ruby的朋友,都可以来试试jvm平台上的这门动态语言,其元编程的强大能力与ruby可一较高下。
(其实,java平台上有各种语言的翻版,例如kotlin对c#,scala对haskell,Clojure对lisp,groovy对ruby,使用这些语言的好处就是可以尽情使用java平台上各种框架和库,毕竟学习java,投资在框架和库上的学习时间也是很多的,如果使用jvm语言的话,这种投资不至于浪费掉)

(ps:我一直觉得,如果使用动态语言而不使用元编程,就失去了使用动态语言的意义。)


大部分的动态语言,其实现面向对象的机制,基本都是原型链(因为我最初学习的是as和js所以习惯这么叫),依靠在原型链上通过字符串的动态查询,得到字段或者方法,在ruby中因为不需要兼容java的关系,其所有的类,字段都是在原型链上,所以甚至都可以在运行时去掉。js甚至还支持字面量对象,即不属性任何类的原始对象。

groovy因为本身同时还支持java式的传统类继承,所以他的原型链,是又搞了一个metaClass出来,这个metaClass保存的是各种扩展方法和字段。

//使用字符串动态调用方法
println "1234"."length"()
println "1234".invokeMethod("length",null)
//动态获取属性
println "1234"["bytes"]
println "1234"."bytes"
//但是这些Java的反射也能做到,没什么大不了的,继续往下看

java自身支持一定的反射,在运行时可以从xx.class对象中查询类的方法和字段,但是并不能修改。因为不能破坏与java的兼容性,所以在groovy中还是可以通过xx.class使用原来java的那套反射方法,但是还可以访问xx.metaClass,概念和这个类似,不过在metaClass中可以添加新的方法。

下面一幅图,很好的说明了字段和方法是如何在运行时被动态查询的,和js和ruby之类的动态语言类似,但是因为groovy需要与java兼容,所以稍微复杂了一些。
这里写图片描述
这幅图看上去颇为繁琐,完成一件事情也有好多种方法。实际上,基本只用一种在其他动态语言中常用的方法就足够了,那就是操作metaClass,而不是在原类上做动作(其实我不明白groovy支持这些额外的路由特性有什么意义)

使用metaClass.invokeMethod来拦截方法调用。

String.metaClass.invokeMethod={
    String name,args->
        System.out.println("调用方法:$name")
        //导向原方法
        Integer.metaClass.getMetaMethod(name,args)?.invoke(delegate,name,args)
}

"1234".length()
//最后会输出 调用方法:length

类似java的动态代理,以及现在火热的aop面向切面编程,但是传统的做法是采用特殊的编译器将代码打入到字节码中,这种直接在语言中编写的做法明显更加灵活。

如果只需要拦截对不存在方法的调用,则只需要覆盖metaClass的methodMissing方法,可以观察上面的动态查询图来了解原因。

在现有类里加入方法,groovy一共有三种方式。
第一种是类似于c#,kotlin的扩展方法

//创建静态类
class Strings{
    def static length2(String str)
    {
        str.length()
    }
}

//使用use来限制作用域
use(Strings){
    //在作用域内,可以像调用扩展方法一样调用类的静态方法
    println "1234".length2()
}

和一般性扩展方法不同的就是,它可以限制作用域。

一般来说,我还是喜欢下面这种动态语言风格的元对象注入

//直接在metaClass上加入方法
String.metaClass.length3={
    delegate.length()
}

println "1234".length3()
//和js的很像
String.prototype.length=function () {

}
//还可以向单独的实例中添加方法,和上面的唯一区别就是只访问实例的metaClass
def str="1234"
str.metaClass.length4={
    delegate.length()
}
println str.length4()

还有一种方法就是所谓的mixin,这种技术的毛病我在之前一篇文章就提过了http://blog.youkuaiyun.com/o83290102o5/article/details/79034497

//定义需要被Mixin的类
class Methods{
    def method1()
    {}

}
@Mixin(Methods)
class TestClass{

}

new TestClass().method1()

上面三种方法,use我基本不用,也没见什么别的语言有这特性,真正需要用到的时候不多。
metaClass上添加属性和方法非常常用。
mixin的话,一般使用的时候需要比较谨慎,否则一不小心就会覆盖掉原来的方法


原型链继承

还是要强调的是,groovy与js和ruby这种动态语言最本质的不同就是,groovy它要考虑对原有java代码的兼容性,所以它的metaClass,只是一个在类继承结构基础上的附带物,就算你把它设置为null,原来类继承中该有的东西还是有,但如果你在js或ruby中这样做,这个类就会脱离原型链,它继承而来的方法会消失,字段也不能访问到了。

//设置metaClass为null,只是动态添加的字段和方法消失,对原来的字段方法无影响
//但是如果在js或者ruby中执行对等的操作,例如js将String.prototype=null,那么所有继承来的字段和方法都会消失
str.metaClass=null
println str.length()

下面是单纯用原型链来实现继承

//我们使用一种类似js的方式来编程
//父类的父类
class Grandfather{
}
Grandfather.metaClass.grandfatherMethod={
    println "grandfather"
}

//父类
class Father{
}
//继承
Father.metaClass.mixin(Grandfather)
//父类方法
Father.metaClass.fatherMethod={
    println "fatherMethod"
}

//上面没有使用传统的类继承结构
//我们现在用原型链继承来模拟继承
//子类
class Child{
}
Child.metaClass.mixin(Father)
//子类方法
Child.metaClass.childMethod={
    println "childMethod"
}

new Child().grandfatherMethod()

(ps:同时支持类继承和原型链继承的语言我还知道一个as3,但是估计很多用过as3的人也不知道它还支持原型链。其实,如果在严格模式下使用原型链继承,编译器会报错,这说明一个语言中共存两种继承模式,本来就相当混乱,不应该提倡)

groovy用起来还是挺不错的,唯一让我觉得没ruby那么舒服的就是,为了和java的兼容性,使得它的动态特性不是那么纯粹

最后推荐一本书《ruby元编程》,虽然讲的不是groovy,但因为groovy和ruby很像,所以对学习元编程也是很有好处的。(况且讲groovy本身的书本来就没几本。)


欢迎关注我的github
https://github.com/luckyCatMiao

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值