嵌套的Groovy闭包产生StackOverflowException

时间:2016-12-15 17:56:25

标签: groovy closures

为什么以下代码会产生StackOverflowException?

​Closure c0 = { 
  println "$this $owner $delegate"
  Closure c1 = {
    println "$this $owner $delegate"
  }
  c1()
}

c0()​

输出

java.lang.StackOverflowError
    at Script1$_run_closure1$_closure2.doCall(Script1.groovy:5)
    at Script1$_run_closure1$_closure2.doCall(Script1.groovy)
    at Script1$_run_closure1.doCall(Script1.groovy:7)
    at Script1$_run_closure1$_closure2.doCall(Script1.groovy:5)
    at Script1$_run_closure1$_closure2.doCall(Script1.groovy)
    at Script1$_run_closure1.doCall(Script1.groovy:7)

2 个答案:

答案 0 :(得分:0)

StackOverFlowError是由内部闭包 所有者 委托 对象。

您可以使用字符串连接而不是字符串插值来访问它们的值。

在内部闭包c1中,你可以这样做:

println "$this " + owner + " " + delegate

这是一个实现此解决方案的link to an example,并且还会删除“这个'”所有者'的内容。并且'委托'对于内部和外部闭合,您可以看到它们是如何不同的。

访问该链接,然后点击“执行”按钮。按钮查看结果。

答案 1 :(得分:0)

如果您像这样分解脚本,则更容易看到正在发生的事情:

There was a failure to launch the remote debugger

然后按照指示在IDE中设置断点,并通过调试器运行它。然后你可以看到内部闭包中的Closure c0 = { println "$this" println "$owner" println "$delegate" Closure c1 = { println "$this" // breakpoint here println "$owner" println "$delegate" } c1() } c0() owner值实际上是外部闭包。

现在,通常,当一个对象被内插到Groovy GString中时,会调用delegate方法,但当插值对象是一个闭包时,情况并非如此。在那个的情况下,调用序列是toString()。因此,闭包object.call().toString()c1最终会在无限循环中相互调用。

在调试器中,你可以单步执行并看到这个效果,从我的第7行步回到第2行(调用c0owner),然后是3,4,5 (定义{{​​1}}),10(调用c0),6,7,再返回2。

为了防止这种情况,请更直接地将闭包强制转换为字符串,如下所示:

c1

或者这个:

c1

或者这个:

    println (owner as String)

(如特雷弗的解决方案)