Groovy has always had the underlying Meta Object Protocol (MOP) that allowed the same behaviour as languages such as Ruby and Small Talk, however the API onto these has, up until now, not been as elegant as it could have been. With Groovy 1.1 beta 3 there is a lot more at your finger tips including...
Runtime evaluation with respondsTo & hasProperty
It is now a lot easier to inspect the runtime thanks to the addition of hasProperty and respondsTo for meta classes so you can do:
class Foo {
String name = "Wilma"
def sayHello() { println "hello $name" }
def sayHello(String message) { println "hello $message" }
}
def f = new Foo()
if(f.metaClass.respondsTo(f,'sayHello') {
f.sayHello()
}
Since Groovy supports method overloading and typed arguments to support good Java interoperability you can also specify a type:
if(f.metaClass.respondsTo(f,'sayHello', String) {
f.sayHello("Fred")
}
Groovy also differentiates method and property access so you can do this:
if(f.metaClass.hasProperty(f,'name') {
println f.name
}
See the docs here for more info on hasProperty and respondsTo
Missing method/property interception with methodMissing & propertyMissing
As of beta 3, Groovy now supports the concept of "method missing". It has always been possible to intercept method dispatch using invokeMethod. However, this has the overhead of intercepting every method call instead of just the "missing" ones. As of Groovy 1.1 beta 3, Groovy now supports "method missing" and "property missing", which are only called when method dispatch fails (ie just before a MissingMethodException would be thrown anyway.)
A trivial example can be seen below, we use this feature in Grails to implement dynamic finders such as findByTitleAndAuthor("Groovy in Action", "Dierk Koenig"):
Foo.metaClass.methodMissing = { String name, args ->
Foo.metaClass."$name" = { Object[] varArgs ->
"$name : ${varArgs.inspect()}"
}
delegate."$name"(args)
}
def f = new Foo()
f.sayHello('Fred')
assert f.notARealMethod("boo")
== 'notARealMethod : [["boo"]]'
assert f.notARealMethod("boo", "hoo")
== 'notARealMethod : [["boo", "hoo"]]'
Notice how in the example above we can dynamically register a new method on the fly and then call that method. This also has the implicationthat the next dispatch to the same method is faster. See the docs here for more info and there are a whole load of docs on doing dynamic Groovy and meta-programming to be found here.
6 comments:
almost as cool as Ruby :-)
seriously: great stuff. Groovy is getting better and better.
Claus
Way to go Graemer! hasProperty() is a feature I was expecting for a quite a while. These changes surelly make Grails development a lot easier ;-)
It is strange, that this functions was missed previously.
Hi Graeme,
Is there also a way to find out the properties of a class without having to create an instance? For example:
class User {
String firstname, lastname
}
println User.metaClass.properties
Also is there some way to 'identify' a property for example User.@firstname?
In Ruby I saw this example of binding:
model = Person.new('Julian Doherty', 'julian@example.com')
name_label = JLabel.new
name_label.bind_to model, :name
It would be cool if the Groovy SwingBuilder binding could work this way. The current binding examples I found bind components together I haven't found any model binding examples. Do you know of any plans to work on this?
Good luck on Groovy/Grails,
Peter
Hi Peter
Your example
println User.metaClass.properties
Will work, the only thing to be aware of is that it returns a list of MetaProperty instances. So for example you can do this:
User.metaClass.properties.each { println p.name }
As for your binding example I know there is a lot of activity going on around SwingBuilder and bindng. Checkout Danno's blog for the info:
http://shemnon.com/speling/
Thanks Graeme indeed User.metaClass.properties works in the latest Groovy version :)
But if you need the type of firstname property I think you need to do:
User.metaClass.properties.grep { it.name == 'firstname'}[0].type
I think it would be a lot 'groovier' if you could do something like:
User.@firstname.type
Maybe I'm wrong and maybe I would better post this in the Groovy mailing list?
Best regards,
Peter
Post a Comment