To close groovy to change the variable defined in the delegate scope, you need to explicitly specify the delegation .theVariableName?

I came across something with Groovy shutting down and delegates whom I am not sure is an official part of the language or possibly even a mistake.

Basically, I define a closure, which I read as a string from an external source, and one of the variables in the class that defines the closure must be modified by the closure. I wrote a simple example showing what I found works and what doesn't work.

If you look at the test code below, you will see a class that defines a variable

animal = "cat" 

and two closures, defined on the go from the lines that try to change the animal variable.

It works>

 String code = "{ -> delegate.animal = 'bear'; return name + 'xx' ; }" 

But it is not

 String code = "{ -> animal = 'bear'; return name + 'xx' ; }" 

It looks like I need to explicitly assign the variable to-be-modified using "delegate". for this to work. (I think I can also define a setter in the enclosing class to close to cause a change in value.)

So ... I found out how to make this work, but I would be interested if someone could point me to some Groovy doc that explains the rules behind it.

In particular .... why a simple appointment

 animal = 'bear' 

affects the original variable? Are there shadow copies here or something else?

 import org.junit.Test /* * Author: cbedford * Date: 8/30/12 * Time: 1:16 PM */ class GroovyTest { String animal = "cat" String name = "fred" @Test public void testDelegateWithModificationOfDelegateVariable() { String code = "{ -> delegate.animal = 'bear'; return name + 'xx' ; }" def shell = new GroovyShell() def closure = shell.evaluate(code) closure.delegate = this def result = closure() println "result is $result" println "animal is $animal" assert animal == 'bear' assert result == 'fredxx' } // This test will FAIL. @Test public void testDelegateWithFailedModificationOfDelegateVariable() { String code = "{ -> animal = 'bear'; return name + 'xx' ; }" def shell = new GroovyShell() def closure = shell.evaluate(code) closure.delegate = this def result = closure() println "result is $result" println "animal is $animal" assert animal == 'bear' assert result == 'fredxx' } } 
+8
scope closures groovy
source share
1 answer

Groovy closures have five strategies for resolving characters within closures:

  • OWNER_FIRST : the owner is checked first (where the restriction is defined), then the delegate
  • OWNER_ONLY : owner checked, delegate checked only if explicitly specified
  • DELEGATE_FIRST : delegate is delegated first, then the owner
  • DELEGATE_ONLY : the delegate is checked first, the owner is checked only if explicitly specified
  • TO_SELF : Neither the delegate nor the owner are marked.

The default is OWNER_FIRST . Since the restriction is determined dynamically, your owner is a Script object that has special rules. Writing animal = 'bear' in Script will actually create a new binding with the name animal and assign it 'bear' .

You can fix your tests to work without explicit reference to the delegate, simply by changing the permission policy to close before calling it:

 closure.resolveStrategy = Closure.DELEGATE_FIRST 

This will avoid the odd Script binding and use the delegate as expected.

+7
source share