0
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() を実行すると)、例外が発生します: Exception in thread "AWT-EventQueue-0" groovy.lang.MissingPropertyException: No such property: content for class: Main

明らかに、何らかの理由で、List が呼び出された場所から、TextBox や Box ではなく、Main で List を検索しています。これも所有者とデリゲートを使用してみましたが、それらはすべてメインも指しています。これを引数として指定することで、なんとか機能させることができました:

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

しかし、クロージャーがどこから呼び出されたのかを知るためだけに、実際に「this」をクロージャーに渡す必要があるのは奇妙に思えます。よりエレガントなソリューションはありませんか?また、TextBox スコープに入ることが実際に不可能な場合でも、Box スコープに入ることができるのでしょうか。

4

2 に答える 2

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が (closure を定義した後b.click) からTextBoxに変更されたことに注意してくださいMain

物事を簡単にするために、で変更する代わりに、次のようにMain変更できますonClick()

void onClick() {
    click.delegate = this
    click()
}

詳細については、このスクリプトを参照してください。

于 2013-05-22T04:58:31.500 に答える