示例1: java
class MyClass{ def hello(){ 'invoked hello directly' } def invokeMethod(String name, Object args){ return "unknown method $name(${args.join(', ')})" } } def mine= new MyClass() assert mine.hello() == 'invoked hello directly' assert mine.foo("Mark", 19) == 'unknown method foo(Mark, 19)'
首先咱們在groovy腳本中定義了一個Myclass對象,在groovy中任何的對象都是實現GroovyObject而且繼承GroovyObjectSupport的,在GroovyObject的接口中,咱們能夠看到幾個方法首先是getMetaClass方法和setMetaClass方法,metaClass用來支持動態方法和動態參數的調用。另外一組方法是getProperty和setProperty方法,這組方法是用來支持動態參數的設定與賦值的。最後還有一個invokeMethod方法,該方法則是用於調用動態方法的。在瞭解了上述概念後,咱們能夠理解爲MyClass的invokeMethod方法覆蓋了GroovyObjectSupport中對應的方法,因此調用未預先定義的foo方法就會進入invokeMethod的實現。而hello方法預先定義則照舊。 數組
示例2: this
class MyClass implements GroovyInterceptable{ def hello(){ 'invoked hello() directly' } def invokeMethod(String name, Object args){ "invoked method $name(${args.join(', ')})" } } def mine = new MyClass() assert mine.hello() == 'invoked method hello()' assert mine.foo('Mark',19) == 'invoked method foo(Mark, 19)' assert mine.&hello() == 'invoked hello() directly'
該例子和示例1的不一樣在於實現了一個GroovyInterceptable接口,仔細看下這個接口的描述,可知道實現該接口的類被調用方法時都是默認使用invokeMethod方法,而無論該方法時動態生成或時靜態生成;第2點不一樣是若是要再此狀況下調用原有定義號的方法,須要變通的使用'.&'操做符。 spa
示例3: 線程
class MyClass{ def greeting = 'accessed greeting directly' Object getProperty(String property){ "read from property $property" } void setProperty(String property, Object newVlaue){ throw new Exception("wrote to property $property") } } def mine = new MyClass() assert mine.greeting == 'read from property greeting' try{ mine.greeting = 'hi' }catch(e){ assert e.message == 'wrote to property greeting' } assert mine.@greeting == 'accessed greeting directly'
該示例描述了屬性的獲取特性,在示例1中已經描述設置和獲取屬性的方法時繼承來的,這裏不作贅述。默認的經過Gpath(見Gpath具體概念)來處理屬性的值,都是經過調用getProperty和SetProperty來代勞的。一樣的若是你真但願直接訪問參數的值,能夠變通的使用'.@'操做符來達成。 code
經過Gpath來得到屬性值。不管該屬性在類中是否有,都是不會出錯的執行那2個方法。可是對於類中不存在的屬性,忌使用'.@'操做符,會拋出MissingFieldException。 對象
示例4: 繼承
class MyClass implements GroovyInterceptable{ def greeting = 'accessed greeting' def id ='White: ' Object getProperty(String property){ try{ return this.@id + //access field directly 'indirectly ' + this.@"$property" }catch(e){ return "no such property $property" } } def hello(Object[] args){"invoked hello with (${args.join(', ')})"} def id(){'Green: '} def invokeMethod(String name, Object args){ try{ return this.&id() + //call method directly 'indirectly ' + this.&"$name"(args) }catch(e){ return "no such method $name" } } } def mine = new MyClass() assert mine.greeting == 'White: indirectly accessed greeting' assert mine.farewell == 'no such property farewell' assert mine.hello(1, 'b', 3) == 'Green: indirectly invoked hello with (1, b, 3)' assert mine.foo('Mark', 19) == 'no such method foo'
該示例是對示例 2,3的一個合併,同時他告訴咱們咱們能夠經過操做符'.@' 或者'.&'後使用雙引號中定義變量的方法來動態的獲取參數或者動態的方法。 接口
示例5: 字符串
public class MyMetaClass extends DelegatingMetaClass{ MyMetaClass(Class thisClass){ super(thisClass) } Object invokeMethod(Object object, String methodName, Object[] arguments){ "MyMetaClass: ${super.invokeMethod(object, methodName, arguments)}" } } class A{ def bark(){'A: invoked bark()'} def invokeMethod(String name, Object args){ "A: missing $name(${args.join(', ')})" } } def amc = new MyMetaClass(A) amc.initialize() def a = new A() a.metaClass = amc assert a.bark() == 'MyMetaClass: A: invoked bark()' Thread.start { assert a.bark() == 'MyMetaClass: A: invoked bark()' } assert new A().bark() == 'A: invoked bark()' assert a.bleet() == 'A: missing bleet()'該示例的代碼較長,主要的意思是咱們能夠經過任意Groovy對象中的metaClass屬性來爲改變該對象的方法調用的行爲。Groovy爲咱們提供了 DelegatingMetaClass 來讓咱們實現該功能。
具體的作法是:首先建立一個自定義的MetaClass類繼承於DelegatingMetaClass,同時實現構造方法,以及自定義的方法調用行爲(之後的內容將介紹,咱們不只能夠改變方法行爲)。而後咱們能夠經過本身建立的metaClass子類來包裝你想改變行爲的目標類。此處爲類A。而後再建立目標類實例,對目標類中得metaClass屬性進行設置。隨後在該實例上的方法調用將會按照該實例的metaClass屬性所對應的處理器,進行處理,該例中是再完成自己方法調用後在前面添加MyMetaClass字符串。
可是值得注意的是,1.對於另外新建立的A實例,若是沒有進行metaClass屬性賦值將按照原方法定義執行;2.動態定義的
方法不受metaClass方法行爲改變的影響。見該示例的最後一行。3.上述的特性在新線程內一樣有效。
示例6:
InvokerHelper.instance.metaRegistry.setMetaClass(A, amc)
該示例只是對示例5的補充,咱們曾提到,咱們能夠經過爲目標類實例設置metaClass屬性來讓metaClass的行爲對該實例生效。可是若是要在類範圍內生效的話,就須要經過上面的代碼進行註冊。這樣註冊事後,將對目標類的全部,任什麼時候候建立的示例,都賦予metaClass的行爲。
這個示例還有一點須要注意。試想這麼一種狀況,先建立了一個目標類實例,再用示例6的語句註冊,再建立一個目標類的實例。metaClass的行爲將在哪一個對象上生效呢。答案是二者都生效。這點很是關鍵。一旦進行了類範圍metaClass註冊,那對於已建立和新建立的對象都生效。
注:對於高版本的groovy InvokerHelper的instance不存在。能夠直接調用metaRegistry。
示例7:
import org.codehaus.groovy.runtime.InvokerHelper; public class MyMetaClass extends DelegatingMetaClass{ MyMetaClass(Class theClass){ super(theClass) } Object invokeConstructor(Object[] arguments){ [] } } class A{} def amc = new MyMetaClass(A) amc.initialize() InvokerHelper.metaRegistry.setMetaClass(A,amc) def a = new A() assert a.class == ArrayList assert (a<<1<<2<<3).size() == 3
在以前的示例已經略有提過metaClass不只能夠改變預約義的方法行爲。在該示例中就以改變構造行爲爲例。該metaClass將構造行爲變成建立一個數組對象。隨後的操做是一目瞭然的。
在此值得一提的是,invokeConstructor方法時從metaClass的接口MetaObjectProtocol繼承過來,其餘的方法可能
是從不一樣的繼承樹而來的,因此能夠參見DelegatingMetaClass中得方法簽名。咱們能夠發現有不少invokeXXX和getXXX的方法,也就是說咱們能夠對這些metaClass行爲作更改。主要的咱們能夠看到有getProperty方法invokeConstructor方法,固然我很興奮的發現還有一個invokeMissingMethod方法,這個方法彷佛就是對咱們示例5中不能處理動態定義方法的metaClass行爲的一個有用的補充了。
下面這個示例內容較多,在給出例子以前,須要先澄清一些細節,你可能會想咱們以前人爲的爲groovyobject設置metaClass來改變類實例的行爲,那默認不設置狀況下這個metaClass是什麼呢? 其實不管是你設置或者沒有設置metaClass它的metaClass都是HandleMetaClass類型的,可是區別在於,裏面有一個getAdaptee方法返回的是DelegatingMetaClass中的MetaClass類型的delegate屬性,默認狀況下,這個delegate會事MetaClassImpl類型的,可是一旦你本身設置了目標類實例的metaClass屬性那這個delegate屬性就是你設置的了。咱們下面要將的這個話題,也就是這一章的標題ExpandoMetaClass(MetaClassImpl的一個子類)正是又一個與MetaClassImpl以及自定義的MetaClass平行的delegate只是這個delegate,支持動態建立方法等等的特性。接着咱們先引入示例
示例8:
class A{ String text } def a1= new A(text: 'aBCdefG') assert a1.metaClass.adaptee.class == MetaClassImpl A.metaClass.inSameCase = {-> text.toUpperCase()} //triggers conversion of MetaClass of A to ExpandoMetaClass //then adds new instance method 'inUpperCase' to class //A.metaClass { } // def a2 = new A(text:'hiJKLmnOp') assert a2.metaClass.adaptee.class == ExpandoMetaClass //MetaClass of A changed for instances created after conversion trigger only assert a2.inSameCase() == 'HIJKLMNOP' //new method not available assert a1.metaClass.adaptee.class == MetaClassImpl try{ println a1.inSameCase();} catch(e){assert e in MissingMethodException} A.metaClass.inLowerCase = {-> text.toLowerCase()} assert a2.inLowerCase() == 'hijklmnop' //replace the method definition with another A.metaClass.inSameCase = {-> text.toLowerCase()} assert a2.inSameCase() == 'hijklmnop' //add static methods A.metaClass.'static'.inSameCase = {it.toLowerCase()} assert A.inSameCase('qRStuVwXyz') == 'qrstuvwxyz'代碼的前幾行印證了,默認的delegate(即HandleMetaClass中得metaClass屬性)是MetaClassImpl。而後咱們調用了 A.metaClass { }方法(該方法位於DefaultGroovyMethods中)使得返回的HandleMetaClass中得delegate是
ExpandoMetaClass類型的。這樣咱們就可以利用該類型的metaClass爲咱們作事了。一樣的
A.metaClass.inSameCase = {-> text.toUpperCase()}方法只是在註冊metaClass爲ExpandoMetaClass的同時
爲其動態添加一個實例方法。同時咱們能夠在最後幾行的代碼中獲知,動態添加靜態方法也是可行的。
幾點須要注意的:1.只有在切換delegate爲ExpandoMetaClass後建立的目標對象才能支持切換時所提供的動態方法。2.動態方法的添加存在覆蓋關係。
示例9:
class A{ } A.metaClass.character = 'Cat in the Hat' def a1 = new A() assert a1.character == 'Cat in the Hat' def ourProperties = Collections.synchronizedMap([:]) A.metaClass.setType = {String value -> ourProperties["${delegate}Type"] = value } A.metaClass.getType = { -> ourProperties["${delegate}Type"]} a1.type = 'Hatted Cat' assert a1.type == 'Hatted Cat' def a2 = new A() A.metaClass.constructor = { -> new A()} try{ a2 = new A() }catch(Error e){ assert e in StackOverflowError } A.metaClass.constructor = {String s -> new A(character :s)} a2 = new A("Thing One") A.metaClass.'changeCharacterToThingTwo'= {-> delegate.character = 'Thing Two' } a2.character= 'Cat in the Hat' a2.changeCharacterToThingTwo() assert a2.character == 'Thing Two' ['Hatted Cat', 'Thing', 'Boy', 'Girl', 'Mother'].each{p-> A.metaClass."changeTypeTo${p}"= {-> delegate.type= p} } a2.changeTypeToBoy() assert a2.type == 'Boy' a2.'changeTypeToHatted Cat'() assert a2.type == 'Hatted Cat'
該示例的內容只要是示例8的一個補充,咱們不只能夠動態添加方法,同時還能夠動態添加屬性和構造方法。
示例10:
ExpandoMetaClass.enableGlobally() //call 'enableGlobally' method before adding to supplied class List.metaClass.sizeDoubled = {-> delegate.size() * 2 } //add method to an interface def list = [] << 1 << 2 assert list.sizeDoubled() == 4該示例比較簡介,旨在告訴咱們咱們不只能夠對自定義的groovy對象進行屬性方法等的動態添加,一樣的咱們能夠對非自定義的Groovy提供的對象進行動態處理。處理方法和自定義對象的方法時徹底同樣的,惟一的區別在於,咱們須要額外定義 ExpandoMetaClass.enableGlobally(),然而筆者發現筆者使用的1.8.1版的groovy若是去掉了該聲明也是能夠工做
因此請各位讀者按照我的版本作嘗試。
示例11:
class Bird{ def name = 'Tweety' def twirp(){ 'i taught i saw a puddy cat' } } Bird.metaClass.invokeMethod = {name, args-> def metaMethod = Bird.metaClass.getMetaMethod(name, args) metaMethod?metaMethod.invoke(delegate,args): 'no such method' } def a = new Bird() assert a.twirp() == 'i taught i saw a puddy cat' assert a.bleet() =='no such method' Bird.metaClass.getProperty = {name-> def metaProperty = Bird.metaClass.getMetaProperty(name) metaProperty?metaProperty.getProperty(delegate): 'no such property' } def b = new Bird() assert b.name == 'Tweety' assert b.filling == 'no such property'
該示例主要說明的是咱們不只能夠用Expando特性來添加方法屬性和構造方法,一樣的咱們能夠對已經存在的方法進行覆蓋。最後還要強調一下Bird.metaClass返回的是ExpandoMetaClass咱們這裏覆蓋的getMetaMethod和
getProperty以及invokeMethod和getProperty方法都是對ExpandoMetaClass類和它父類對應的方法進行覆蓋。
至此,Groovy的MetaClass內容已經介紹完了。該部份內容在groovy中很是重要。謹此記錄下來做爲往後參考,同時但願對你們有幫助。