关闭检查导致意外的堆栈溢出错误

Closure examination causing unexpected Stack Overflow Error

我正在尝试使用此代码来更好地理解 thisownerdelegate 如何在 Groovy 闭包中工作:

class GroovyTest {
    static void main(String[] args) {
        examine() {
            println 'In first closure'
            println "class is ${getClass().name}"
            println "this is $this, super: ${this.getClass().getSuperclass().name}"
            println "owner is $owner, super: ${owner.getClass().getSuperclass().name}"
            println "delegate is $delegate, super: ${delegate.getClass().getSuperclass().name}"

            examine() {
                println 'In closure within the closure'
                println "class is ${getClass().name}"
                println "this is $this, super: ${this.getClass().getSuperclass().name}"
                println "owner is $owner, super: ${owner.getClass().getSuperclass().name}"
                println "delegate is $delegate, super: ${delegate.getClass().getSuperclass().name}"
            }
        }
    }

    static examine(closure) {
        closure()
    }
}

当我执行这段代码时,它会导致堆栈溢出错误,但是我很难找出导致无限递归的原因。我在这里包含了部分堆栈跟踪:

Caught: java.lang.WhosebugError
java.lang.WhosebugError
    at GroovyTest$_main_closure1.doCall(GroovyTest.groovy:10)
    at jdk.internal.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at GroovyTest$_main_closure1$_closure2.doCall(GroovyTest.groovy:14)
    at GroovyTest$_main_closure1$_closure2.doCall(GroovyTest.groovy)
    at jdk.internal.reflect.GeneratedMethodAccessor8.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at GroovyTest.examine(GroovyTest.groovy:21)
    at jdk.internal.reflect.GeneratedMethodAccessor7.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at GroovyTest$_main_closure1.doCall(GroovyTest.groovy:10)
    at jdk.internal.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at GroovyTest$_main_closure1$_closure2.doCall(GroovyTest.groovy:14)
    at GroovyTest$_main_closure1$_closure2.doCall(GroovyTest.groovy)
    at jdk.internal.reflect.GeneratedMethodAccessor8.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at GroovyTest.examine(GroovyTest.groovy:21)

似乎是行 println "owner is $owner, super: ${owner.getClass().getSuperclass().name}" 引起了无限递归。

为什么此代码会导致堆栈溢出错误,我该如何解决?

Edit:以下脚本也包含嵌套闭包,完全按预期工作。堆栈溢出异常不是由嵌套闭包本身引起的。好像跟打印语句有关系

def examiningClosure(closure) {
  closure()
}

examiningClosure() { 
  println "In First Closure:"
  println "class is " + getClass().name
  println "this is " + this + ", super:" + this.getClass().superclass.name
  println "owner is " + owner + ", super:" + owner.getClass().superclass.name
  println "delegate is " + delegate +
              ", super:" + delegate.getClass().superclass.name
  
  examiningClosure() { 
    println "In Closure within the First Closure:"
    println "class is " + getClass().name
    println "this is " + this + ", super:" + this.getClass().superclass.name
    println "owner is " + owner + ", super:" + owner.getClass().superclass.name
    println "delegate is " + delegate +
                ", super:" + delegate.getClass().superclass.name
  }  
}

好的,首先我要为没有花时间仔细研究这个问题而道歉。

将尝试用实际解释来弥补它。

原来是下面的代码:

def c = {
  println "foo"
}

println "${c}"

有点不直观地导致闭包被执行,而不是仅仅在标准输出上打印它的字符串表示:

─➤ groovy test.groovy
foo

所以在你的例子中:

class GroovyTest {
    static void main(args) {
        examine { // closure 1
            examine { // closure 2
                println "${owner}"
            }
        }
    }

    static examine(closure) {
        closure()
    }
}

(examine方法调用后的括号可以省略)

最里面的闭包的owner就是最里面的闭包本身。调用封闭闭包将导致自调用的无限递归,最终导致 WhosebugException.

您可以通过例如 println "${owner.getClass()}" 来解决此问题,这会阻止隐式闭包调用。

我不得不说来自 "${closure}" 的闭包调用非常不直观,根本不是我所期望的......经过多年使用 groovy......但它就是这样是。