I'm trying to use this code to better understand how this
, owner
, and delegate
work in Groovy Closures:
class GroovyTest {
static void main(String[] args) {
examine() {
println 'In first closure'
println "class is ${getClass().name}"
println "this is $this, super: ${this.getClass().getSuperclass().name}"
println "owner is $owner, super: ${owner.getClass().getSuperclass().name}"
println "delegate is $delegate, super: ${delegate.getClass().getSuperclass().name}"
examine() {
println 'In closure within the closure'
println "class is ${getClass().name}"
println "this is $this, super: ${this.getClass().getSuperclass().name}"
println "owner is $owner, super: ${owner.getClass().getSuperclass().name}"
println "delegate is $delegate, super: ${delegate.getClass().getSuperclass().name}"
}
}
}
static examine(closure) {
closure()
}
}
When I execute this code, it results in a Stack Overflow Error, however I am having difficulty figuring out what is causing the infinite recursion. I'm including part of the stack trace here:
Caught: java.lang.StackOverflowError
java.lang.StackOverflowError
at GroovyTest$_main_closure1.doCall(GroovyTest.groovy:10)
at jdk.internal.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at GroovyTest$_main_closure1$_closure2.doCall(GroovyTest.groovy:14)
at GroovyTest$_main_closure1$_closure2.doCall(GroovyTest.groovy)
at jdk.internal.reflect.GeneratedMethodAccessor8.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at GroovyTest.examine(GroovyTest.groovy:21)
at jdk.internal.reflect.GeneratedMethodAccessor7.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at GroovyTest$_main_closure1.doCall(GroovyTest.groovy:10)
at jdk.internal.reflect.GeneratedMethodAccessor9.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at GroovyTest$_main_closure1$_closure2.doCall(GroovyTest.groovy:14)
at GroovyTest$_main_closure1$_closure2.doCall(GroovyTest.groovy)
at jdk.internal.reflect.GeneratedMethodAccessor8.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at GroovyTest.examine(GroovyTest.groovy:21)
It appears that the line println "owner is $owner, super: ${owner.getClass().getSuperclass().name}"
is causing the infinite recursion.
Why is this code causing a Stack Overflow Error, and what can I do to fix it?
Edit: The following script, which also contains nested closures, works exactly as intended. The Stack Overflow Exception isn't being caused by the nested closures itself. It seems like it has something to do with the print statement.
def examiningClosure(closure) {
closure()
}
examiningClosure() {
println "In First Closure:"
println "class is " + getClass().name
println "this is " + this + ", super:" + this.getClass().superclass.name
println "owner is " + owner + ", super:" + owner.getClass().superclass.name
println "delegate is " + delegate +
", super:" + delegate.getClass().superclass.name
examiningClosure() {
println "In Closure within the First Closure:"
println "class is " + getClass().name
println "this is " + this + ", super:" + this.getClass().superclass.name
println "owner is " + owner + ", super:" + owner.getClass().superclass.name
println "delegate is " + delegate +
", super:" + delegate.getClass().superclass.name
}
}
Ok I'll start by apologizing for not taking the time to look into this properly.
Will try to make up for it with an actual explanation.
Turns out that following code:
def c = {
println "foo"
}
println "${c}"
somewhat unintuitively results in the closure being executed instead of just getting a string representation of it printed on std out:
─➤ groovy test.groovy
foo
So in the case of your example:
class GroovyTest {
static void main(args) {
examine { // closure 1
examine { // closure 2
println "${owner}"
}
}
}
static examine(closure) {
closure()
}
}
(parens after the examine
method call can be omitted)
the owner
of the innermost closure is the innermost closure itself. Calling the enclosing closure will result in the infinite recursion of self-calls and eventually a StackOverflowException
.
You can fix this by for example instead do println "${owner.getClass()}"
which prevents the implicit closure call.
I would have to say that the closure call from "${closure}"
is quite unintuitive and not at all what I expected...after years and years of using groovy...but there it is.