如何获取在Groovy中调用闭包的类(范围)?

时间:2013-05-21 22:30:18

标签: groovy closures

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-scope?

2 个答案:

答案 0 :(得分:1)

请参阅closures groovy文档。注意隐式变量:this,owner和delegate。
修改: 在通话前修复委托:

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

答案 1 :(得分: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已从b.click更改为TextBox之后更改Main

为了简化操作,您可以将Main修改为

,而不是更改onClick()中的内容。
void onClick() {
    click.delegate = this
    click()
}

有关详细信息,请参阅this script