Reputation: 79
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
}
}
(The above is, obviously, a simplification; in reality, the classes are a bit more involved, and calling of onClick() is due to a click on a JFrame)
Now, when I try to run that (i.e. run Main.main()), I get an exception: Exception in thread "AWT-EventQueue-0" groovy.lang.MissingPropertyException: No such property: content for class: Main
Clearly, it is, for some reason, searching for the List in Main, not in TextBox or Box, from where it is called. I also tried using this, owner and delegate, but they all point to Main as well. I managed to have it work by giving this as an argument:
...
void onClick() {
click(this)
}
...
b.click = {it.content.add("You clicked this box!")}
It seems, however, weird to actually need to pass "this" to a closure just for it to be able to know where it was called from. Isn't there a more elegant solution? Also, even if it is indeed impossible to get into the TextBox-scope, is it somehow possible to get into the Box-scope?
Upvotes: 0
Views: 216
Reputation: 3552
See closures groovy docs. Pay attention on Implicit variables: this, owner, and delegate.
Edit:
Fix delegate before call:
b.click = {content.add("You clicked this box!")}
b.click.delegate = b
Upvotes: 1
Reputation: 50245
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!]
Points to note:
TextBox
but Sequence 4 prints Main
. Note that the delegate
has changed (after defining closure b.click
) from TextBox
to Main
by now.To make things easier, instead of changing things in Main
, you can just modify onClick()
as
void onClick() {
click.delegate = this
click()
}
Refer this script for details.
Upvotes: 1