I have a groovy file with the following 3 statements:
def mySillClosure = { null }
def returnValue = mySillClosure.call()
println returnValue
This prints
null
as expected.
If I modify the file and add a closure definition at the end as follows:
def mySillClosure = { null }
def returnValue = mySillClosure.call()
println returnValue
{ String foo -> foo.toLowerCase() }
I am starting to get:
Caught: java.lang.NullPointerException: Cannot invoke method call() on null object
java.lang.NullPointerException: Cannot invoke method call() on null object
at mygroovy.run(mygroovy.groovy:3)
How does defining a closure changing the behaviour for a previous line?
BTW, if I only have the following only in the file, it will run fine - but will not print anything:
{ String foo -> foo.toLowerCase() }
It looks like returnValue is treated as a method that accepts a closure. In groovy if you omit parentheses ( the last argument is the only argument in this case - its a closure), it will treat this closure like a method argument:
println returnValue { String foo -> foo.toLowerCase() }
is Basically the same as:
println returnValue({String foo -> foo.toLowerCase()})
// its a method that accepts a closure as a parameter
But as you said, the method is null by itself, so it can't really call the closure.
For example the following code will do the job:
def method(Closure s) {
s.call("HELLO")
}
println method { String foo -> foo.toLowerCase() }
// or, just the same:
println method({ String foo -> foo.toLowerCase() })
Both will print a lower-cased: hello
Related
I am trying to make my own Dsl and was playing around with different styles of closures in groovy.
I've stumbled upon follwing snippet:
myClosure {
testProperty: "hello!"
}
But can't figure out if this a valid code and how can I access then this testProperty. Is it valid? How can I read "hello!" value?
For now, lets put aside closures, consider the following code:
def f1() {
testProperty : 5
}
def f2() {
testProperty : "Hello"
}
println f1()
println f1().getClass()
println f2()
println f2().getClass()
This compiles (therefor the syntax is valid) and prints:
5
class java.lang.Integer
Hello
class java.lang.String
So what you see here is just a labeled statement (groovy supports labels see here)
And bottom line the code of f1 (just like f2) is:
def f1() {
return 5 // and return is optional, so we don't have to write it
}
With closures its just the same from this standpoint:
def method(Closure c) {
def result = c.call()
println result
println result.getClass()
}
method {
test : "hello"
}
This prints
hello
class java.lang.String
as expected
Usually in DSL you have either this:
mySomething {
a = 42
b = 84
}
which corresponds to property setting
or this:
mySomething( a:42, b:84 ){
somethingElse{}
}
which is a method call with Map-literal.
The code you shown is not used as #mark-bramnik explained.
I have a simple script, which runs and works:
println testReturn()
String testReturn() {
def str = /asdf/
return str
}
If I change it to this, however, I see an error when I run it:
println testReturn()
String testReturn() {
return /asdf/
}
Error:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
test.groovy: 6: unexpected token: / # line 6, column 12.
return /asdf/
^
1 error
Does anyone know why I have to define a slash-escaped string before returning it?
It seems to me that the return statement is treated like other function calls in groovy 2.x and that slashy strings do not work well with paren-less function calls.
An example:
println /asdf/ // fails
println(/asdf/) // works
println testReturn()
String testReturn() {
/asdf/ //works - implicit return
}
String btestReturn() {
return /asdf/ // fails
}
String testReturn() {
return(/asdf/) //works
}
in my book this is unexpected behavior and a restriction of the groovy parser.
In groovy 3 (or more specifically in this case, groovy 3.0.0-alpha-3), the parser has been rewritten and the following code:
println /asdf/ // fails
println(/asdf/) // works
println testReturn1()
println testReturn2()
println testReturn3()
String testReturn1() {
/asdf/ // works
}
String testReturn2() {
return /asdf/ // works
}
String testReturn3() {
return(/asdf/) // works
}
gives the somewhat more informative error:
Caught: groovy.lang.MissingPropertyException: No such property: println for class: solution
groovy.lang.MissingPropertyException: No such property: println for class: solution
at solution.run(solution.groovy:1)
which tells us that without parens, groovy can not determine if println is a property or a method call. Removing that first line makes the above code work under groovy 3.x.
I need to intercept method calls on predefined Java classes. For example, lets say I need to intercept String class split method, how do I do this?
I tried this which works, but I doesn’t want end user to change their code by wrapping their calls in with proxy block.
Is there any way this can be achieved with Groovy?
If what you want to do is intercept a call to a specific method you can do something like this...
// intercept calls to the split method on java.lang.String
String.metaClass.split = { String arg ->
// do whatever you want to do
}
If what you want to do is intercept a call to a specific method and do some stuff in addition to invoking the original (like to wrap the real method with some of your own logic) you can do something like this:
// get a reference to the original method...
def originalSplit = String.metaClass.getMetaMethod('split', [String] as Class[])
// now add your own version of the method to the meta class...
String.metaClass.split = { String arg ->
// do something before invoking the original...
// invoke the original...
def result = originalSplit.invoke(delegate, arg)
// do something after invoking the original...
// return the result of invoking the original
result
}
I hope that helps.
you want to use MetaClass for that see doc
ExpandoMetaClass.enableGlobally()
//call 'enableGlobally' method before adding to supplied class
String.metaClass.split = { regex ->
println "calling split from $delegate with $regex"
delegate.split regex, 22
}
To intercept all method calls in a class override Groovy's invokeMethod. Example:
class Test {}
Test.metaClass.foo = {"foo() called"}
Test.metaClass.static.bar = {"bar() called"}
Test.metaClass.invokeMethod = { name, args ->
handleInterception(name, args, delegate, false)
}
Test.metaClass.static.invokeMethod = { name, args ->
handleInterception(name, args, delegate, true)
}
def handleInterception(name, args, delegate, isStatic) {
def effDelegate = isStatic ? delegate : delegate.class
println ">> Entering ${delegate.class.name}.$name() with args: $args"
def metaMethod = effDelegate.metaClass.getMetaMethod(name, args)
if (!metaMethod) {
println "-- Method not found: $name($args)"
return
}
try {
def result = metaMethod.invoke(delegate, args)
println "<< Leaving ${delegate.class.name}.$name() with result: $result"
return result
} catch (ex) {
println "-- Exception occurred in $name: $ex.message"
throw ex
}
}
new Test().foo("1", 2)
Test.bar(2)
new Test().onTheFly(3)
Code taken from Roshan Dawrani's post at groovyconsole.appspot.com.
Output:
>> Entering Test.foo() with args: [1, 2]
-- Method not found: foo([1, 2])
>> Entering java.lang.Class.bar() with args: [2]
<< Leaving java.lang.Class.bar() with result: bar() called
>> Entering Test.onTheFly() with args: [3]
-- Method not found: onTheFly([3])
Other options:
Custom MetaClass implementing invokeMethod
Implementing the Interceptor Interface. Read more in this tutorial
I'm implementing Groovy step definitions for Cucumber-JVM and I want a step to be able store itself so that the next step can repeat it n times.
Given(~'the (\\S+) is in the blender') { String thing ->
// do stuff...
context.repeatable = self.curry(thing)
}
What should "self" be in the above code?
I can't use "this" as that refers to the enclosing object (whatever that is in this case, maybe the script).
Since curry is a method of the Closure class, directly invoking curry applies to the closure, both if it is named:
def context
test = { String thing ->
context = curry(thing)
thing.toUpperCase()
}
test('hello')
println context.call()
test('world')
println context.call()
=>
HELLO
WORLD
or anonymous:
def context
['test'].each { String thing ->
context = curry(thing)
thing.toUpperCase()
}
println context.call()
=>
TEST
You can try using unreferenced curry method passing the received parameters:
clos = { String text ->
if (text) {
println "text=$text"
a = curry null
a()
} else {
println "done"
}
}
clos "closure text"
Will print:
text=closure text
done
Update
You can also use clone():
closure = {
def clone = clone()
}
My understanding of the Groovy operator .& is that it converts a method call to a closure. Therefore it seems like the following code (which can be run in the Groovy console) should work:
class Foo {
def method(def param) {
param + 10
}
}
def invokeClosure = {Closure closure ->
return closure.call()
}
def f = new Foo()
invokeClosure f.&method(6)
Of course if I change the last line to
invokeClosure {f.method(6)}
it works fine, but what's wrong with my understanding of the .& operator?
Thanks,
Don
When converting a method to a closure using .& notation you leave off the parameters. f.&method(6) is the same as calling f.method(6) which will return 16, so in your example you are passing 16 into invokeClosure and not a closure. Which causes the following exception since the Integer class doesn't have a call method:
Exception thrown: No signature of method: java.lang.Integer.call()
Below passes a method pointer for f.method into invokeClosure and would be how you would typically use .&.
class Foo {
def method(def param) {
param + 10
}
}
def invokeClosure = {Closure closure ->
return closure.call(6) // can leave off .call
}
def f = new Foo()
invokeClosure f.&method
As you pointed out the following will work:
invokeClosure {f.method(6)}
That's because you are passing a closure that takes no parameters so that's why closure.call() works in that case.
Use invokeClosure f.&method.curry(6) instead. This is a closure which could be called without parameters
The example above could also be extended to take the parameter as argument into the invokeClosure. That would give you the expected result and syntax.
class Foo {
def method(def param) {
param + 10
}
}
def invokeClosure = {Closure closure, def parameter ->
return closure.call(parameter)
}
def f = new Foo()
invokeClosure f.&method, 6