Abhishek Kumar Abhishek Kumar - 2 months ago 8
Groovy Question

What are delegate objects?

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.greeting = "My custom greeting"


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"

//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.


What is a closure's delegate?

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"

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.

When are delegates used?

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) {
        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 method defined anywhere within MarkupBuilder called records 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.