Groovy Closures: this, owner, delegate let’s make a DSL
Groovy closures are super cool. To fully understand them, I think it’s really important to understand the meaning of this, owner and delegate. In general:
- this: refers to the instance of the class that the closure was defined in.
- owner: is the same as this, unless the closure was defined inside another closure in which case the owner refers to the outer closure.
- delegate: is the same as owner. But, it is the only one that can be programmatically changed, and it is the one that makes Groovy closures really powerful.
Confused? Let’s look at some code.
class MyClass { def outerClosure = { println this.class.name // outputs MyClass println owner.class.name // outputs MyClass println delegate.class.name //outputs MyClass def nestedClosure = { println this.class.name // outputs MyClass println owner.class.name // outputs MyClass$_closure1 println delegate.class.name // outputs MyClass$_closure1 } nestedClosure() } } def closure = new MyClass().closure closure()
With respect to above code:
- The this value always refers to the instance of the enclosing class.
- owner is always the same as this, except for nested closures.
- delegate is the same as owner by default. It can be changed and we will see that in a sec.
So what is the point of this, owner,delegate? Well remember, that closures are not just anonymous functions. If they were we could just call them Lambdas and we wouldn’t have to come up with another word, would we?
Where closures go beyond lambdas is that they bind or “close over” variables that are not explicitly defined in the closure’s scope. Again, let’s take a look at some code.
class MyClass { String myString = "myString1" def outerClosure = { println myString; // outputs myString1 def nestedClosure = { println myString; // outputs myString1 } nestedClosure() } } MyClass myClass = new MyClass() def closure = new MyClass().outerClosure closure() println myClass.myString
Ok, so both the closure and the nestedClosure have access to variables on the instance of the class they were defined in. That’s obvious. But, how exactly do they resolve the myString reference? Well it’s like this. If the variable was not defined explicitly in the closure, the this scope is then checked, then the owner scope and then the delegate scope. In this example, myString is not defined in either of the closures, so groovy checks their this references and sees the myString is defined there and uses that. Ok, let’s take a look at an example where it can’t find a variable in the closure and can’t find it on the closure’s this scope, but it can find’s it in the closure’s owner scope.
class MyClass { def outerClosure = { def myString = "outerClosure"; def nestedClosure = { println myString; // outputs outerClosure } nestedClosure() } } MyClass myClass = new MyClass() def closure = new MyClass().closure closure()
In this case, Groovy can’t find myString in the nestedClosure or in the this scope. It then checks the owner scope, which for the nestedClosure is the outerClosure. It finds myString there and uses that. Now, let’s take a look at an example where Groovy can’t find a variable in the closure, or on this or the owner scope but can find it in on closure’s delegate scope. As discussed earlier the owner delegate scope is the same as the owner scope, unless it is explicitly changed. So, to make this a bit more interesting, let’s change the delegate.
class MyOtherClass { String myString = "I am over in here in myOtherClass" } class MyClass { def closure = { println myString } } MyClass myClass = new MyClass() def closure = new MyClass().closure closure.delegate = new MyOtherClass() closure() // outputs: "I am over in here in myOtherClass"
The ability to have so much control over the lexical scope of closures in Groovy gives enormous power. Even when the delegate is set it can be change to something else, this means we can make the behavior of the closure super dynamic.
class MyOtherClass { String myString = "I am over in here in myOtherClass" } class MyOtherClass2 { String myString = "I am over in here in myOtherClass2" } class MyClass { def closure = { println myString } } MyClass myClass = new MyClass() def closure = new MyClass().closure closure.delegate = new MyOtherClass() closure() // outputs: I am over in here in myOtherClass closure = new MyClass().closure closure.delegate = new MyOtherClass2() closure() // outputs: I am over in here in myOtherClass2
Ok, so it should be a bit clearer now what this owner and delegate actually correspond to. As stated, the closure itself will be checked first, followed by the closure’s this scope, than the closure’s owner, then its delegate. However, Groovy is so flexible this strategy can be changed. Every closure has a property called resolvedStrategy. This can be set to:
- Closure.OWNER_FIRST
- Closure.DELEGATE_FIRST
- Closure.OWNER_ONLY
- Closure.DELEGATE_ONLY
So where is a good example of a practical usage of the dynamic setting of the delegate property. Well you see in the GORM for Grails. Suppose we have the following domain class:
class Author { String name static constraints = { name size: 10..15 } }
In the Author class we can see a constraint defined using what looks like a DSL whereas in the Java / Hibernate world we would not being able to write an expressive DSL and instead use an annotation (which is better than XML but still not as neat as a DSL). So, how come we can use a DSL in Groovy then? Well it is because of the capabilities delegate setting on closures adds to Groovy’s metaprogramming toolbox. In the Author GORM object, constraints is a closure, that invokes a name method with one parameter of name size which has the value of the range between 10 and 15. It could also be written less DSL’y as:
class Author { String name static constraints = { name(size: 10..15) } }
Either way, behind the scenes, Grails looks for a constraints closure and assigns its delegate to a special object that synthesizes the constraints logic. In pseudo code, it would be something like this…
// Set the constraints delegate constraints.delegate = new ConstraintsBuilder(); // delegate is assigned before the closure is executed. class ConstraintsBuilder = { // // ... // In every Groovy object methodMissing() is invoked when a method that does not exist on the object is invoked // In this case, there is no name() method so methodMissing will be invoked. // ... def methodMissing(String methodName, args) { // We can get the name variable here from the method name // We can get that size is 10..15 from the args ... // Go and do stuff with hibernate to enforce constraints } }
So there you have it. Closures are very powerful, they can delegate out to objects that can be set dynamically at runtime. That plays an important part in Groovy’s meta programming capabilities which mean that Groovy can have some very expressive DSLs.
Reference: | Groovy Closures: this, owner, delegate let’s make a DSL from our JCG partner Alex Staveley at the Dublin’s Tech Blog blog. |