【问题标题】:How do I get the class (scope) from which a closure is called in Groovy?如何获取在 Groovy 中调用闭包的类(范围)?
【发布时间】:2025-11-29 09:10:02
【问题描述】:
class Box {
    Closure click

    Box () {
        click = {}
    }

    void onClick() {
        click()
    }
}

class TextBox extends Box {
    List<String> content

    TextBox (String[] a) {
        super()
        content = a
    }
}

class Main {
    public static void main(String[] args) {
        Main m = new Main()
    }

    Main() {
        String[] a = ["Hello world!"]
        Box b = new TextBox(a)
        b.click = {content.add("You clicked this box!")}
        b.onClick()  //throws Exception
    }
}

(显然,上面是一个简化;实际上,这些类涉及更多,并且 onClick() 的调用是由于单击 JFrame)

现在,当我尝试运行它(即运行 Main.main())时,我得到一个异常: 线程“AWT-EventQueue-0”groovy.lang.MissingPropertyException 中的异常:没有这样的属性:类的内容:Main

很明显,由于某种原因,它是在 Main 中搜索 List,而不是在 TextBox 或 Box 中搜索它的调用位置。我也尝试过使用它,所有者和委托,但它们也都指向 Main。 我设法通过将其作为参数来使其工作:

...
void onClick() {
    click(this)
}
...
b.click = {it.content.add("You clicked this box!")}

然而,实际上需要将“this”传递给一个闭包,以便它能够知道它是从哪里调用的,这似乎很奇怪。没有更优雅的解决方案吗?另外,即使确实不可能进入 TextBox 范围,是否有可能进入 Box 范围?

【问题讨论】:

    标签: groovy closures


    【解决方案1】:
    Main() {
        String[] a = ["Hello world!"]
        Box b = new TextBox(a)
        println "1:- $b.click.delegate" //Will print TextBox
        b.click = {
            println "2:- $b.click.delegate" //Will print Main
    
            //Since the closure is now defined inside Main(), 
            //Main becomes the delegate. Reset the delegate to TextBox.
            b.click.delegate = b
    
            println "3:- $b.click.delegate" //Will print TextBox
            content.add("You clicked this box!")
        }
        println "4:- $b.click.delegate" //Will print Main
        b.onClick()
    
        println b.content //Will print [Hello world!, You clicked this box!]
    }
    
    //Output:
    1:- TextBox@c166770
    4:- Main@6dbdc863
    2:- Main@6dbdc863
    3:- TextBox@c166770
    [Hello world!, You clicked this box!]
    

    注意事项:

    • 序列 4 出现在 2 和 3 之前,因为在那个时间点没有调用/调用闭包。
    • 序列 1 打印 TextBox 但序列 4 打印 Main。请注意,delegate 现在已经从TextBox 更改为Main(在定义闭包b.click 之后)。

    为了让事情变得更简单,您可以将onClick()修改为

    ,而不是更改Main
    void onClick() {
        click.delegate = this
        click()
    }
    

    详情请参考this script

    【讨论】:

    • 啊,好的。这确实比使用“它”更优雅。虽然我仍然需要传递“this”(click.delegate = this),但我想如果不这样做是不可能的。非常感谢!
    • Yw。如果您没有正确设置delegate,这是不可能的,这就是闭包的本质。此外,我们没有将任何东西传递给闭包,我们正在设置它的委托属性。 :)(坚持闭包使用父对象而不是被调用者)。
    【解决方案2】:

    请参阅closures groovy 文档。注意隐式变量:this、owner 和delegate。
    编辑: 调用前修复委托:

    b.click = {content.add("You clicked this box!")}
    b.click.delegate = b
    

    【讨论】:

    • 正如我所说:“我也尝试过使用它,所有者和委托人,但它们也都指向 Main。”