I am currently working on writing gradle build scripts. What are delegate objects and when are they used?
class GroovyGreeter {
String greeting = "Default greeting"
def printGreeting(){println "Greeting: $greeting"}
}
def myGroovyGreeter = new GroovyGreeter()
myGroovyGreeter.printGreeting()
myGroovyGreeter.greeting = "My custom greeting"
myGroovyGreeter.printGreeting()
/*
The last Groovy feature we'll cover is that closures can have a delegate
object. Any variables or methods referenced in the closure that don't have a
local definition are then evaluated against the closure's delegate. Let's make
a closure that will access the property and method of our GroovyGreeter class.
*/
def greetingClosure = {
greeting = "Setting the greeting from a closure"
printGreeting()
}
//greetingClosure() // This doesn't work, because `greeting` isn't defined
greetingClosure.delegate = myGroovyGreeter
greetingClosure() // This works as `greeting` is a property of the delegate
Please help me out.
When you set the delegate of a closure to another object, any calls to properties/methods which cannot be resolved within the closure's scope, will be resolved by the closure's delegate. In your example, greeting
and printGreeting
are undefined within the scope of the greetingClosure
closure
def greetingClosure = {
greeting = "Setting the greeting from a closure"
printGreeting()
}
So when you set the delegate of this closure to an instance of GroovyGreeter
greetingClosure.delegate = myGroovyGreeter
they are (successfully) resolved by this object because it does define a property and method with these names.
They are typically used in DSLs or builders, when you want the user of the DSL/builder to be able to call methods with any name, and the name of the method is used within the DSL almost like an additional method parameter. Take the following example of generating XML using a builder
def writer = new StringWriter()
def xml = new MarkupBuilder(writer)
xml.records() {
car(name:'HSV Maloo', make:'Holden', year:2006) {
country('Australia')
record(type:'speed', 'Production Pickup Truck with speed of 271kph')
}
}
def records = new XmlSlurper().parseText(writer.toString())
The first thing we do after creating the builder is call records
passing it a closure argument. There isn't actually a records
method defined anywhere within MarkupBuilder
that takes a closure argument, but all undefined methods will be routed to the MarkupBuilder
, using Groovy's methodMissing
feature.
Within the closure that is passed to records
we call various other methods that are not defined at compile-time, e.g. car
. How are these method calls resolved by MarkupBuilder
? Because the delegate of this closure is set to the MarkupBuilder
instance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With