Why does printing the owner of a closure in a string cause infinite recursion?

不想你离开。 提交于 2019-12-20 01:43:44

问题


I'm playing with closures and seeing this odd behavior that I can't quite explain:

groovy:000> ({ println owner })()
groovysh_evaluate@200b6145
===> null
groovy:000> ({ println "${owner}" })()
groovysh_evaluate@2bf75a70
===> null
groovy:000> ({ ({ println owner })() })()
groovysh_evaluate$_run_closure1@10f67a01
===> null
groovy:000> ({ ({ println "${owner}" })() })()
ERROR java.lang.StackOverflowError:
null
        at groovysh_evaluate$_run_closure1_closure2.doCall (groovysh_evaluate:2)
        at groovysh_evaluate$_run_closure1_closure2.doCall (groovysh_evaluate)
        at groovysh_evaluate$_run_closure1.doCall (groovysh_evaluate:2)
        at groovysh_evaluate$_run_closure1_closure2.doCall (groovysh_evaluate:2)
        at groovysh_evaluate$_run_closure1_closure2.doCall (groovysh_evaluate)
        at groovysh_evaluate$_run_closure1.doCall (groovysh_evaluate:2)
        <stacktrace repeats>

I figure it has something to do with the fact that the ${} is itself a closure, but I can't quite pin down why this happens. The issue does appear related to accessing owner as I've not seen it happen with other variables/expressions. Any ideas?


回答1:


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()}"



来源:https://stackoverflow.com/questions/17904170/why-does-printing-the-owner-of-a-closure-in-a-string-cause-infinite-recursion

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!