Groovy探索之MOP 七 运行期内的方法和属性分析
在Groovy语言里,运行期内的方法和属性分析有三种方式,它们分别是:
- 第一, 继承自Java语言的反射方式。
- 第二, 使用"respondsTo"和"hasProperty"方法。
- 第三, 使用"hasMetaMethod"和"hasMetaProperty"方法。
以上三种方法都能在运行期内分析某个方法或属性是否存在,相信我们看到这里,一定会想,它们之间是否有什么区别呢?
漫谈这三种运行期内的方法和属性分析方式以及它们之间的区别,正是我们本篇所要做的事情。
在Groovy语言的类中,方法和属性通常是一对并列的概念,也就是说,如果有了一个针对类方法的方法,那么必然有一个相对应的类属性的方法。就像上面所提到的"respondsTo"和"hasProperty","hasMetaMethod"和"hasMetaProperty"。因此,这几组对应的方法的用法大体相似。
所以,在本篇中,限于篇幅的限制,都以运行期内的方法分析来作为例子来说明以上三组方法的使用以及它们之间的区别,而对于运行期内的属性分析则不做说明。因为这些运行期内的属性分析的方法是使用和区别大体上与方法的使用和区别相似。
首先,我们来看看使用Java语言的反射来分析运行期内的方法。比如,我们有如下的一个类:
class Testor3 {
def test1()
{
println 'test1'
}
}
我们很容易使用反射来分析运行期内的方法,如下所示代码:
def method = Testor3.class.getMethod('test1',null)
这样的代码是能够正常编译和运行的。
但是,如果我们有如下的代码:
Testor3.metaClass."test2" = {
->
println 'test2'
}
def method = Testor3.class.getMethod('test2',null)
那么运行它,就会报如下的错误:
Exception in thread "main" java.lang.NoSuchMethodException: Testor3.test2()
at java.lang.Class.getMethod(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
这说明,使用反射不能找到在运行期内使用Groovy语言的MOP特性给类添加的方法。当然,属性也是这样。
接着,我们来看看"respondsTo"方法的使用。假设我们有如下的一个类:
class Testor1 {
def bar()
{
println 'bar'
}
}
那么,我们就可以这样来使用"respondsTo"方法:
Testor1.metaClass."far" = {
->
println 'far'
}
def t1 = new Testor1()
if(t1.metaClass.respondsTo(t1,'bar'))
{
println 'yes'
}
if(t1.metaClass.respondsTo(t1,'far'))
{
println 'yeah'
}
运行结果为:
yes
yeah
上面的结果说明,"respondsTo"方法可以分析类固有的方法、以及通过Groovy语言的MOP属性在运行期内给类添加的方法。
最后,我们来看看"hasMetaMethod"方法的使用。现在我们有如下的类:
class Testor2 {
def test1()
{
println 'test1'
}
}
那么,我们就可以如下使用"hasMetaMethod"方法:
Testor2.metaClass."test2" = {
->
println 'test2'
}
if(Testor2.metaClass.getMetaMethod('test1'))
{
println 'ok1'
}
if(Testor2.metaClass.getMetaMethod('test2'))
{
println 'ok2'
}
运行结果为:
ok1
ok2
从上面的运行结果,可以看出,"hasMetaMethod"方法同样可以找出类固有的方法和运行期内通过Groovy语言MOP特性给类添加的方法。
那么,"respondsTo"方法和"hasMetaMethod"方法又有什么区别呢?
我们来看这样的一个测试,假设我们有如下的一个类:
class Testor4 {
def test1()
{
println 'test1'
}
}
我们来写如下的代码:
def t1 = new Testor4()
def emc = new ExpandoMetaClass( t1.class, false )
emc.test2 = { println "test2" }
emc.initialize()
t1.metaClass = emc
if(t1.metaClass.respondsTo(t1,'test2'))
{
println 'ok'
}
if(Testor4.metaClass.getMetaMethod('test2'))
{
println 'ok1'
}
现在可以猜猜看,上面代码的运行结果是什么呢?
在上面的测试代码中,我们使用Groovy语言的MOP特性给t1对象在运行期内添加了一个方法"test2",然后,我们就分别使用"respondsTo"方法和"hasMetaMethod"方法来测试这个新添的方法。
运行结果为:
ok
现在,我们可以知道"respondsTo"方法和"hasMetaMethod"方法的区别了:"respondsTo"方法可以找到一个类固有的方法,使用MOP特性在运行期内给类添加的方法,和使用MOP特性在运行期内给对象添加的方法。而"hasMetaMethod"方法则只能找到一个类固有的方法,和使用MOP特性在运行期内给类添加的方法,而不能找到使用MOP特性在运行期内给对象添加的方法。
最后,我们来总结一下三种运行期内分析方法的方式:使用反射,我们只能找到一个类固有的方法;使用"hasMetaMethod"方法则能够找出一个类固有的方法、以及使用Groovy语言MOP特性在运行期内给一个类添加的方法;而"respondsTo"方法不但能够找出一个类固有的方法、使用Groovy语言MOP特性在运行期内给一个类添加的方法,而且能够找出使用Groovy语言MOP特性在运行期内给一个对象添加的方法。