简体   繁体   English

在 Groovy 闭包中调用 Owner 时的递归

[英]Recursion when calling Owner in Groovy Closure

I have been trying to understand Groovy scripts but an example I made is hitting an unexpected StackOverflow.我一直在尝试理解 Groovy 脚本,但我举的一个例子遇到了意外的 StackOverflow。

The foo closure prints out its owner as 'Tank', before defining another closure named bar.在定义另一个名为 bar 的闭包之前,foo 闭包将其所有者打印为“Tank”。 I expected bar to print the owner as something like Tank$foo.我希望 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()
    }
}

Well... The short answer is you should change your bar method to the following:好吧......简短的回答是你应该将你的bar方法更改为以下内容:

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

That will prevent the foo closure referenced by the $owner from evaluation.这将防止评估$owner引用的foo闭包。 Because it will no longer be a closure but rather a string representation of it.因为它将不再是一个闭包,而是它的一个字符串表示。 This is what you want to achieve in your example.这就是您想在示例中实现的目标。

The full answer is bit too complicated and has its history...完整的答案有点太复杂并且有它的历史......

String interpolation in Groovy is implemented using the GString class. When the compiler comes across your " Owner is $owner" it first breaks that value into different pieces where the " Owner is " is kept as a string whereas $owner goes to the so-called values. Groovy 中的字符串插值是使用GString class 实现的。当编译器遇到您的" Owner is $owner"时,它首先将该值分解为不同的部分,其中" Owner is "作为string保存,而$owner转到 -称为价值观。 This is how an instance of GString gets constructed , it's a combination of those two pieces.这就是GString实例的构造方式,它是这两个部分的组合。

If we then look into the Groovy source code, we can see how the GString.toString() is evaluated.如果我们查看 Groovy 源代码,我们可以看到GString.toString()是如何求值的。 The existing implementation iterates over the both arrays, strings and values , and if the value is a closure then it just eagerly evaluates (calls) that.现有的实现遍历 arrays、 stringsvalues ,如果value是一个闭包,那么它只是急切地评估(调用)那个。 This is why you fall into the infinite recursion in your example.这就是为什么您陷入示例中的无限递归。 Here is the snippet of GString.toString() found in Groovy source code (spot the c.call(...) ):这是在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);
}

Interestingly, I also found when this was first introduced and the discussion about potentially changing this behaviour.有趣的是,我还发现这是什么时候首次引入的,以及关于可能改变这种行为的讨论。 The discussion issue is still open.讨论问题仍未解决。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM