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

I play with closing and see this strange behavior that I can not 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 believe this is because ${} alone is a closure, but I cannot understand why this is happening. The problem arises in connection with owner access, as I have not seen this happening with other variables / expressions. Any ideas?

+4
source share
1 answer

In cases where closure is embedded inside GString, toString() not called on closure, unlike variables built into GString. In the above case, when you see an error, owner is closing and toString() will not be called when closing.

To get around this, toString() needs to be explicitly called owner as follows:

({ ({ println "${owner.toString()}" })() })()

The same applies for the large number of nested closing levels that we create.

 ({ ({ ({ println "${owner.toString()}" })() })() })() 

With the right indentation, it will look like this:

 ({ ({ ({ println "${owner.toString()}" })() })() })() 

The behavior can be explained by a trivial example.

 def clos = {return {"hello"}} println "${clos()}" //prints nothing println "${clos()()}" //prints hello 

Explanation for error: -
Now, when it comes to the error that was mentioned earlier, when closing is built inside GString, toString() not called when closing. Instead, a closure is called, and then toString() is called / applied to the result of the called closure. It means:

"$owner" equivalent to owner().toString() .

In the above case, causing an external closure, it ultimately calls itself through the implementation of GString [ "$owner" ], and the call grows like a recursion, hence a stackoverflow error.

Note:
You can omit {} when the variable you are applying GString to is simple. "${myVariable}" matches "$myVariable" . You can do this while you access the simple properties of a variable. "$myVariable.class.name" is good (as long as myVariable is not a map). But when method calls are involved, curly braces "${myVariable.toString()}" are needed

+4
source

Source: https://habr.com/ru/post/1493872/


All Articles