在Groovy闭包中调用所有者时的递归

ahy6op9u  于 2022-11-01  发布在  其他
关注(0)|答案(1)|浏览(145)

我一直在尝试理解Groovy脚本,但我做的一个示例遇到了意外的StackOverflow。
foo闭包在定义另一个名为bar的闭包之前,将其所有者打印为'Tank'。我希望bar将所有者打印为类似Tank$foo的内容。

class Tank {
    def foo = {
        println "Owner is $owner"

        def bar = {
            println "      Owner is $owner"
        }
        bar()
    }

    static void main(String ...args){
        def t = new Tank() ;
        t.foo()
    }
}
h9a6wy2h

h9a6wy2h1#

简单来说,您应该将bar方法更改为以下内容:

def bar = {
    println "      Owner is ${owner.toString()}"
}

这将阻止$owner引用的foo闭包被求值,因为它将不再是闭包,而是闭包的字符串表示。这就是你在示例中想要实现的。

完整答案有点太复杂了,有它的历史......

Groovy中的字符串插值是使用GString类实现的。当编译器遇到" Owner is $owner"时,它首先将该值拆分为不同的片段,其中" Owner is "保留为string,而$owner则指向所谓的值。这就是GString的示例的构造方式,它是这两个片段的组合。
如果我们接着查看Groovy源代码,我们可以看到GString.toString()是如何计算的。如果value是一个闭包那么它就急切地求出这就是为什么在示例中会陷入无限递归的原因。下面是在Groovy源代码中找到的GString.toString()的片段(请注意c.call(...)):

if (value instanceof Closure) {
    final Closure c = (Closure) value;

    if (c.getMaximumNumberOfParameters() == 0) {
        InvokerHelper.write(out, c.call());
    } else if (c.getMaximumNumberOfParameters() == 1) {
        c.call(out);
    } else {
        throw new GroovyRuntimeException("Trying to evaluate a GString containing a Closure taking "
                + c.getMaximumNumberOfParameters() + " parameters");
    }
} else {
    InvokerHelper.write(out, value);
}

有趣的是,我还发现当这是第一次介绍和讨论可能改变这种行为。讨论问题仍然是开放的。

相关问题