Property Dispatch on Groovy Objects
In my last post, I covered Method Dispatch on Groovy Objects. The next topic is similar, Property Dispatch.
In Groovy, you can specify properties on objects in the same way that you can specify fields in Java: With the “.” operator. I.e., if I have an object named “Test”, and it has a class variable named “test”, you can access it with the phrase “Test.test” (just like Java).
As I already mentioned in my Introduction to Groovy, Groovy provides automatic creation of accessors and mutators (get and set). That’s actually an oversimplification, but we’ll leave it at that for now. As a result, consider the following Groovy class:
class GetSet { def firstField = 1 }
This can be used in the following way, inside a Java class:
GetSet gs = new GetSet(); System.out.println(gs.getFirstField()); gs.setFirstField("test"); System.out.println(gs.getFirstField());
That’s because, under the covers, void setFirstField(Object)
and Object getFirstField()
are added to the Groovy class. Note that because the class field is declared as def
, the type of these messages is Object
.
While that’s handy in Java, in Groovy, we can do so much more with properties. You can access them with dot notation:
def gs = new GetSet() gs.firstField = 2.5 println gs.firstField
You can access them with get/setProperty (which is part of GroovyObject
, the base object for all objects in Groovy):
def gs = new GetSet() gs.setProperty('firstField','test2') println gs.getProperty('firstField')
And you can even access a Map of all the properties on the object, and operate on that:
def gs = new GetSet() println gs.properties.firstField
This last example is interesting since it’s using a few features of Groovy: the properties
property is actually a shortcut to the getProperties()
method, which returns a Map
. In turn, you can access the values of the map by using the key in dot notation. So this is actually a shortcut for the following Java code:
GetSet gs = new GetSet(); System.out.println(gs.getProperties().get("firstField"));
While all that’s handy, that’s still not dispatch. In order to handle properties dispatch, there are two different methods we can use. Consider the following code:
GetSet gs = new GetSet(); gs.newprop = 'new value' println gs.newprop
newprop doesn’t exist in GetSet – we want to handle adding it at runtime. The first method to do so is to override missingProperty(String)
and missingProperty(String, Object)
. The first method handles gets of properties that don’t exist, the second handles sets. So the following code handles unknown properties seamlessly:
Map props = [:] def propertyMissing(String name, value) { props[name] = value } def propertyMissing(String name) { return props[name] }
In Groovy, Maps can be initialized to be empty with [:]
and the values in that map can be accessed with the syntax pattern map[key]
. So, in this code, if the property doesn’t exist, it will be added into an internal Map if you are setting it, and that value will be returned when the property is later used. If the property is accessed before it’s set, a null is returned. Of course, we could check for existence, and throw an exception if we desired to.
This method will only add to the existing properties, however. If there’s already a property (a class instance field, for instance) on the class, such as firstField in our example, this code is never touched.
If instead you want to completely override the access to fields, you can instead override getProperty(String)
, setProperty(String, Object)
and getProperties()
for good measure, if desired. Here’s an example:
Map props = [:] void setProperty(String name, value) { props[name] = value } Object getProperty(String name) { return props[name] } Map getProperties() { return props }
This code actually does almost the same thing as the previous example, except that now, any instance or class fields are hidden from property access. Meaning that the following code prints null
:
GetSet gs = new GetSet(); println gs.firstField
But note that the following code will still print 1:
GetSet gs = new GetSet(); println gs.@firstField
This is because we’re using the “Java field accessor” in the second example – the .@
notation bypasses get/setProperty methods, and directly act on the underlying Java field.
Dispatch is just a short step away from the above code. Instead of using the internal Map of properties, you could instead route the property lookup anywhere – an xml file, a database, or any other class or computation.
So, now that we’ve looked at method and property dispatch, we’ll run through some ways to use these in a DSL next time. Until then…
Leave a Reply