Renato
Renato

Reputation: 13690

How to get closure delegate to be used correctly when called within a Trait

I have a Groovy trait which needs to provide a config Closure as an argument to a library method call (it's HttpBuilder but it shouldn't matter).

For reproducing the issue, I created the following simple example:

trait T {
    def doIt() {
        return {
            n = 1
        }
    }
}

class Delegate {
    int n
}

class Tish implements T {
    def go() {
        def closure = doIt()
        def d = new Delegate()
        closure.delegate = d
        closure()
        assert d.n == 1
    }
}

new Tish().go()

This is expected to run without errors because when the closure returned by the doIt() method in the T trait is run, its delegate is set to something that can set the n variable to 1....

However, this does not work and I get this error:

 groovy.lang.MissingPropertyException: No such property: n for class: Tish

If I make T a class and let Tish extend it instead, then it works!

I tried changing the Closure's delegate strategy but that did not help.

Is this a Groovy bug or is there a way to work around this issue?

Upvotes: 5

Views: 1149

Answers (2)

Renato
Renato

Reputation: 13690

Alright, I found a workaround... Still, would be interesting to know if this is a bug, and if it is, when it will get fixed by the Groovy team!

UPDATE: this is a bug and hopefully will be fixed in a Groovy release in the near future!

All calls made within the config Closure can get the actual Delegate object by calling the getDelegate() method, then setting all properties directly on it, like this:

return {
    def d = getDelegate()
    d.n = 1
}

Not ideal, but got me unstuck, hope it helps others...

EDIT: As pointed about by @bdkosher in the comments, another solution is to use setter syntax in the closure:

return {
    setN 1
}

Upvotes: 2

Marc Schmid
Marc Schmid

Reputation: 1336

Change your code from

def d = new Delegate()
closure.delegate = d
closure()

to

def d = new Delegate()
closure.delegate = d
closure.run()

this solved a pretty similar issue, that i had with Grails 3.3.8, Groovy 2.4.15, Java 1.8.0_131. No idea why but it helped.

I was trying to chase down the error and added the following outputs

println this
println owner
println delegate

and figured out that in the first case delegate was not changed even closure.delegate = d was set before executing closure()! If closure.run() is called then delegate was set correctly!?!

Upvotes: 0

Related Questions