In this code, the assertion is failing:
class Foo {
def foo() { 'foo' }
}
def foo() { 'foo2' }
def bar(#DelegatesTo(strategy=Closure.DELEGATE_ONLY, value=Foo) Closure c) {
c.delegate = new Foo()
c()
}
assert bar { foo() } == 'foo'
I expected the assertion to pass because the strategy is specified as Closure.DELEGATE_ONLY, and so it should only be trying to resolve foo() using the delegate which is new Foo(), otherwise throw an exception. I didn't think I'd have to specify the strategy explicitly again in the body of the function with c.resolveStrategy = Closure.DELEGATE_ONLY. That just seems verbose. Is this expected behavior?
Related
I am trying to call a method inside another and I am getting a MissingMethodException. My code looks something like this:
class Foo {
static def bar {
//do stuff
return something
}
static def baz { myVar=false ->
def something = bar()
//do stuff w/something
}
}
Foo.baz()
and this is the error I get: Caught: groovy.lang.MissingMethodException: No signature of method: Foo$__clinit__closure6.bar() is applicable for argument types: () values: []
I found that I can fix this error if I define baz like this instead:
static def baz { myVar=false ->
def something = this.bar()
//do stuff w/something
}
but if I do that, my IDE tells me that this is an Unnecessary qualified reference.
What's going on here?
That syntax in your snippet is wrong. Either you wanted static def bar = {} or static def bar() {}.
If you wanted bar to be a method, then there is no error:
class Foo {
static bar() { 'something' }
static baz = { myVar=false ->
def something = bar()
return something + "!"
}
}
assert Foo.baz() == "something!"
If you want bar to be a closure (i.e. bar = {}) then to invoke bar from baz you need to put the class name first:
class Foo {
static bar = { 'something' }
static baz = { myVar=false ->
def something = Foo.bar()
return something + "!"
}
}
assert Foo.baz() == "something!"
I have a closure that's executed against another Groovy object as the delegate. I can do:
foo {
bar {
major = 1
}
}
but when I do:
foo {
bar {
major 1
}
}
I get an error:
> No signature of method: my.Bar.major() is applicable for argument types (java.lang.Integer) values: [1]
Possible solutions: setMajor(java.lang.Integer), getMajor(), wait(), any(), setMajor(java.lang.String), wait(long)
Bar looks something like:
class Bar {
Integer major
Integer getMajor() { return this.major }
def setMajor(Integer val) { this.major = val }
}
I thought Groovy made getters/setters transparent when dealing with property references and that referring to bar.major was the same as bar.get/setMajor(). Am I understanding this wrong, or does the meta class lookup route differ when you throw Closure delegates into the mix? The resolution strategy is DELEGATE_FIRST.
For more context: http://forums.gradle.org/gradle/topics/groovy-no-signature-of-method-running-closure-against-delegate
you would have to add also void major(Integer val). major = 1 is groovy-short for setMajor(1) while major 1 is short for major(1). (see Section Optional parenthesis)
Optional parenthesis
Method calls in Groovy can omit the parenthesis if there is at least one parameter and there is no ambiguity.
println "Hello world"
System.out.println "Nice cheese Gromit!"
E.g.:
class X {
Integer major
void major(Integer m) { major = m }
}
def x = new X()
x.major = 1 // x.setMajor(1)
assert x.major==1 // x.getMajor()==1
x.major 2 // x.major(2)
assert x.major==2
If you need this behaviour alot, you can add a methodMissing for this case. E.g.:
class X {
Integer major
def methodMissing(String name, args) {
if (this.hasProperty(name) && args.size()==1) {
this."$name" = args[0]
} else {
throw new MissingMethodException(name, this.class, args)
}
}
}
def x = new X()
x.major = 1
assert x.major==1
x.major 2
assert x.major==2
A class implements call method so that it's objects can be called as a method. This works for most of the case but not when the call is being made inside a closure on a object which is instance variable of a class.
To demonstrate the problem, in the code below I've commented the interesting lines with numbers. While most variants result in same output, only the line with comment 5 doesn't work. It throws groovy.lang.MissingMethodException: No signature of method: Client2.instanceVar() is applicable for argument types: () values: [])
Can someone help me understand the reason? Is it a bug?
class CallableObject {
def call() { println "hello" }
}
class Client {
def instanceVar = new CallableObject()
def method() {
def localVar = new CallableObject()
def closure1 = { localVar() }
def closure2 = { instanceVar.call() }
def closure3 = { instanceVar() } // doesn't work
localVar() // 1
instanceVar() // 2
closure1() // 3
closure2() // 4
closure3() // 5
}
}
new Client().method()
I guess this will make it clear.
class CallableObject {
def call() { println "hello" }
}
class Client {
def instanceVar = new CallableObject()
def getInstanceVar() {
println "Getter Called"
instanceVar
}
def method() {
def localVar = new CallableObject()
def closure1 = { localVar() }
def closure2 = { instanceVar.call() }
def closure3 = { this.#instanceVar() } //should work now
localVar() // 1
instanceVar() // 2
closure1() // 3
closure2() // 4
closure3() // 5
}
}
new Client().method()
You will see "Getter Called" printed when closure2() invoked. For a global property to be accessed in the closure inside a method, the getter in called instead. To surmount the error you get, the field instanceVar needs to be accessed directly in order to implicitly use call().
In the Groovy console, version 2.2.1:
Why does this work?
class C {
def foo = { "foo" }
def bar = { foo() }
}
new C().bar()
but this fails?
class C {
String foo = { "foo" }
String bar = { foo() }
}
new C().bar()
The above was answered by tim_yates but I have something somewhat related that doesn't seem like it's worth creating a new question for (not sure of the etiquette). When I make them static it also fails when I call bar(). Why does the bar closure not capture foo?
class C {
static foo = { "foo" }
static bar = { foo() }
}
C.foo() //works
C.bar() //fails
Because neither { "foo" } or { foo() } are Strings?
They are Closure<String>
Try:
class C {
Closure<String> foo = { "foo" }
Closure<String> bar = { foo() }
}
new C().bar()
Is there a way to pass a method as a parameter in Groovy without wrapping it in a closure? It seems to work with functions, but not methods. For instance, given the following:
def foo(Closure c) {
c(arg1: "baz", arg2:"qux")
}
def bar(Map args) {
println('arg1: ' + args['arg1'])
println('arg2: ' + args['arg2'])
}
This works:
foo(bar)
But if bar is a method in a class:
class Quux {
def foo(Closure c) {
c(arg1: "baz", arg2:"qux")
}
def bar(Map args) {
println('arg1: ' + args['arg1'])
println('arg2: ' + args['arg2'])
}
def quuux() {
foo(bar)
}
}
new Quux().quuux()
It fails with No such property: bar for class: Quux.
If I change the method to wrap bar in a closure, it works, but seems unnecessarily verbose:
def quuux() {
foo({ args -> bar(args) })
}
Is there a cleaner way?
.& operator to the rescue!
class Quux {
def foo(Closure c) {
c(arg1: "baz", arg2:"qux")
}
def bar(Map args) {
println('arg1: ' + args['arg1'])
println('arg2: ' + args['arg2'])
}
def quuux() {
foo(this.&bar)
}
}
new Quux().quuux()
// arg1: baz
// arg2: qux
In general, obj.&method will return a bound method, i.e. a closure that calls method on obj.
In addition to the method pointer operator (.&), for Groovy version 3.0.0 and above there's the equivalent, compatible, well known Java 8+ method reference operator (::).
def foo(Closure c) {
c(func: "foo")
}
def bar(Map args = [:]) {
println "$args.func bar"
}
println foo(this::bar)
Output:
foo bar
The method reference operator, as stated in Groovy's documentation:
[…] overlaps somewhat with the functionality provided by Groovy’s
method pointer operator. Indeed, for dynamic Groovy, the method
reference operator is just an alias for the method pointer operator.
For static Groovy, the operator results in bytecode similar to the
bytecode that Java would produce for the same context.