为什么在字符串中打印闭包的所有者会导致无限递归?

时间:2013-07-28 01:35:04

标签: groovy closures stack-overflow

我正在玩闭包,看到这种奇怪的行为,我无法解释:

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>

我认为它与${}本身就是一个闭包这一事实有关,但我无法确定为什么会发生这种情况。该问题似乎与访问owner有关,因为我没有看到它与其他变量/表达式一起发生。有什么想法吗?

1 个答案:

答案 0 :(得分:4)

如果在GString中嵌入了闭包,则不会在闭包中调用toString(),而不像GString中嵌入的变量。在上面你看到错误的情况下,owner是周围的闭包,并且在闭包上不会调用toString()

要解决此问题,必须在toString()上明确调用owner,如:

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

同样适用于我们构建的多个嵌套级别的闭包。

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

通过适当的缩进,它看起来像:

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

可以用一个简单的例子来解释这种行为。

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

错误说明: -
现在遇到面临的错误,如前所述,当一个闭包嵌入在GString中时,关闭时不会调用toString()。而是调用闭包,然后在被调用闭包的结果上调用/应用toString()。这意味着:

"$owner"相当于owner().toString()

在上面的例子中,在调用外部闭包时,它最终通过GString实现["$owner"]调用自身,并且调用增长为递归,因此发生堆栈溢出错误。

注意:
当您应用GString的变量很简单时,可以省略{}"${myVariable}""$myVariable"相同。只要您访问变量的简单属性,就可以这样做。 "$myVariable.class.name"很好(只要myVariable不是地图)。但是当涉及方法调用时,需要大括号"${myVariable.toString()}"