I\'m playing with closures and seeing this odd behavior that I can\'t quite explain:
groovy:000> ({ println owner })()
groovysh_evaluate@200b6145
===>
In cases when a closure is embedded inside a GString, toString()
is not called on the closure unlike variables embedded in GString. In the above case where you see an error, owner
is the surrounding closure and toString()
would not be called on the closure.
To get around with it, toString()
has to be called explicitly on owner
like:
({ ({ println "${owner.toString()}" })() })()
The same is applicable for as many nested level of closures we build.
({ ({ ({ println "${owner.toString()}" })() })() })()
With proper indentation it would look like:
({
({
({
println "${owner.toString()}"
})()
})()
})()
The behavior can be explained with a trivial example.
def clos = {return {"hello"}}
println "${clos()}" //prints nothing
println "${clos()()}" //prints hello
Explanation for error:-
Now coming to the error that is faced, as mentioned earlier, when a closure is embedded inside GString, toString()
is not called on the closure. Instead the closure is called and then toString()
is invoked/applied on the result of the called closure. Which means:
"$owner"
is equivalent to owner().toString()
.
In the above case, while calling the outer closure it eventually calls itself through the GString implementation ["$owner"
] and the call grows as a recursion, hence a stackoverflow error.
Note:
You can omit {}
when the variable on which you are applying GString is simple. "${myVariable}"
is same as "$myVariable"
. You can do that as long as you are accessing simple properties of the variable. "$myVariable.class.name"
is good (as long as myVariable is not a map). But when method calls are involved curly braces are needed "${myVariable.toString()}"