Invoke method as closure - groovy

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

Related

Why is this Groovy script failing?

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

Is it possible to pass a Groovy closure defined as a variable to a function for execution, with another variable as part of the passed call?

As the title says, if I define a closure that takes one argument can I pass it with an argument to be executed.
For example:
def print = { String NAME -> println "$NAME"}
And then pass it to another function for execution like this:
otherFunction(print("Jeff"))
If the other function has signature:
otherFunction(Closure clos):
clos.call()
Thanks!
I have figured out where I went wrong, I need my function to return a closure with my variable interpolated.
For example:
def Closure print (String NAME){
{name -> println name}
}
Then make a call to generate a custom closure and pass that:
otherFunction(print("Jeff"))
Answered my own question please close.
The problem with the call otherFunction( print("Jeff") ) is that you pass to it the return value of print("Jeff"), which is null because println does not return anything (void function).
Instead of this, you must pass the closure object which is layzy called with the method call(). You figured out yourself this, but my approach is more straightforward. Other solution would be to use function composition:
def print = { println it }
def otherFunction(Closure clos) {
clos.call()
}
// this is equivalent with otherFunction(null)
//otherFunction( print("Jeff") )
// pass a closure object
otherFunction { print("Jeff") }
// using function composition
def printJeff = print << { "Jeff" }
printJeff()

Is there a way to intercept all method calls in Groovy?

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

Pass method as parameter in Groovy

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.

Is it possible to get the caller object of a closure in groovy?

Is it possible to get a reference of the Object that invoked a Closure in the Closure's execution context?
For example:
public class Example {
public Example(){
def a = {return this};
def b = [];
b.metaClass.a = a;
println b.a();
}
}
I want this execution to return b instead of an instance of Example.
The object that the closure is invoked on can be referenced as delegate. Example:
def a = { return delegate }
def b = []
b.metaClass.a = a
assert b.a() == b

Resources