在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特性在運行期內給一個對象添加的方法。