Source From Here
Using ExpandoMetaClass to add behaviour
Groovy includes a special MetaClass called an ExpandoMetaClass that allows you to dynamically add methods, constructors, properties and static methods using a neat closure syntax.
How does it work? Every java.lang.Class is supplied with a special "metaClass" property that will give you a reference to an ExpandoMetaClass instance. For example to obtain the ExpandoMetaClass of the java.lang.String class you use:
This adds a method called
swapCase to the String class. By default ExpandoMetaClass doesn't do inheritance. To enable this you must callExpandoMetaClass.enableGlobally() before your app starts such as in the main method or servlet bootstrap.
Further Reading:
Borrowing Methods from other classes
With ExpandoMetaClass, you can also use Groovy's method pointer syntax to borrow methods from other classes. For example:
Adding constructors
Adding constructors is a little different to adding a method with ExpandoMetaClass. Essentially you use a special "constructor" property and either use the << or= operator to assign a closure. The arguments to the closure are of course the constructor arguments.
Execution result:
Be careful when adding constructors however, as it is very easy to get into stack overflow troubles. For example below code will overrides the default constructor:
The above would produce a
StackOverflowError as it rescursively keeps calling the same constructor through Groovy's MetaClass system. You can get around this by writing helper code to instantiate an instance outside of Groovy. For example this uses Spring's BeanUtils class and does not cause a StackOverflow:
Supplement
* Annotation Type ToString
Using ExpandoMetaClass to add behaviour
Groovy includes a special MetaClass called an ExpandoMetaClass that allows you to dynamically add methods, constructors, properties and static methods using a neat closure syntax.
How does it work? Every java.lang.Class is supplied with a special "metaClass" property that will give you a reference to an ExpandoMetaClass instance. For example to obtain the ExpandoMetaClass of the java.lang.String class you use:
- String.metaClass.swapCase = {->
- def sb = new StringBuffer()
- delegate.each {
- sb << (Character.isUpperCase(it as char) ? Character.toLowerCase(it as char) :
- Character.toUpperCase(it as char))
- }
- sb.toString()
- }
Further Reading:
Borrowing Methods from other classes
With ExpandoMetaClass, you can also use Groovy's method pointer syntax to borrow methods from other classes. For example:
- package test
- class Person {
- String name
- }
- class MortgageLender {
- def action = "Buy house"
- MortgageLender(){}
- MortgageLender(def act){this.action=act}
- def borrowMoney() {
- action
- }
- }
- def lender1 = new MortgageLender()
- def lender2 = new MortgageLender("Buy car")
- Person.metaClass.buyHouse = lender1.&borrowMoney
- def p = new Person()
- assert "Buy house" == p.buyHouse()
- Person.metaClass.buyHouse = lender2.&borrowMoney // Dynamically switch
- assert "Buy car" == p.buyHouse()
Adding constructors is a little different to adding a method with ExpandoMetaClass. Essentially you use a special "constructor" property and either use the << or= operator to assign a closure. The arguments to the closure are of course the constructor arguments.
- package test
- import groovy.transform.ToString
- @ToString
- class Book {
- String title
- public Book(String title){this.title=title.toUpperCase()}
- @Override
- public String toStrong(){return String.format("Book:${title}")}
- }
- def b = new Book("The Stand")
- printf("${b}\n")
- // Book.metaClass.constructor << { String title -> new Book(title:title) } --> Exception:
- // Cannot add new constructor for arguments [[class java.lang.String]]. It already exists!
- Book.metaClass.constructor << { String title, String ver -> new Book(String.format("%s(%s)", title, ver)) }
- b = new Book("The Stand", "v1.0")
- printf("${b}\n")
Be careful when adding constructors however, as it is very easy to get into stack overflow troubles. For example below code will overrides the default constructor:
- import groovy.transform.ToString
- class Book {
- String title
- }
- Book.metaClass.constructor = { new Book() }
- // Exception in thread "main" java.lang.StackOverflowError
- def b = new Book("The Stand")
- class Book {
- String title
- }
- Book.metaClass.constructor = { BeanUtils.instantiateClass(Book) }
- def b = new Book("The Stand")
Supplement
* Annotation Type ToString
沒有留言:
張貼留言