Asked  10 Months ago    Answers:  5   Viewed   6 times

Suppose I have this class:

class MyClass {
    int myInt

    MyClass(myInt) {
        this.myInt = myInt
    }

    def myMethod() {
        print this.myInt
    }
}

And somewhere I have:

def myClass1 = new MyClass(1)
def myMethodClosure = myClass1.&myMethod
def myClass2 = new MyClass(2)

Now if I call myMethodClosure() it will call myMethod() on myClass1 instance which will print 1. What I want is to call the same myMethodClosure but on a different instance, in this case on myClass2 so it can print 2. Is this possible?

I have tried using setDelegate(), but it does not work. I have also seen that there is field thisObject inside the closure class, but it does not have a setter, only a getter.

 Answers

2

There were two methods added to Groovy to aid serialization of Closures, dehydrate and rehydrate. Basically, they strip (and reconstruct) a Closure's owner, thisObject and delegate. In this example, you could do:

myMethodClosure.rehydrate( myClass2, myClass2, myClass2 )()

To get the output 2, however I'd be wary about doing this as it is not what the method was intended for and there could be serious unforeseen consequences.

A better solution would probably be to write a factory method that gets a method reference for the given instance of MyClass. There may be other -- better -- solutions, but it depends on the situation you are in (that I suspect is not shown by the example in the question)

Thursday, August 19, 2021
3

As of PHP7, you can do

$obj = new StdClass;
$obj->fn = function($arg) { return "Hello $arg"; };
echo ($obj->fn)('World');

or use Closure::call(), though that doesn't work on a StdClass.


Before PHP7, you'd have to implement the magic __call method to intercept the call and invoke the callback (which is not possible for StdClass of course, because you cannot add the __call method)

class Foo
{
    public function __call($method, $args)
    {
        if(is_callable(array($this, $method))) {
            return call_user_func_array($this->$method, $args);
        }
        // else throw exception
    }
}

$foo = new Foo;
$foo->cb = function($who) { return "Hello $who"; };
echo $foo->cb('World');

Note that you cannot do

return call_user_func_array(array($this, $method), $args);

in the __call body, because this would trigger __call in an infinite loop.

Thursday, April 1, 2021
 
5

I'd nearly always prefer the local variable solution.

Memory footprint

A single local variable costs 4 or 8 bytes. It's a reference and there's no recursion, so let's ignore it.

Performance

If this is a simple getter, the JVM can memoize it itself, so there's no difference. If it's a expensive call which can't be optimized, memoizing manually makes it faster.

Readability

Follow the DRY principle. In your case it hardly matters as the local variable name is character-wise as about as long as the method call, but for anything more complicated, it's readability as you don't have to find the 10 differences between the two expressions. If you know they're the same, so make it clear using the local variable.

Correctness

Imagine your SelectItem does not accept nulls and your program is multithreaded. The value of listType.getDescription() can change in the meantime and you're toasted.

Debugging

Having a local variable containing an interesting value is an advantage.


The only thing to win by omitting the local variable is saving one line. So I'd do it only in cases when it really doesn't matter:

  • very short expression
  • no possible concurrent modification
  • simple private final getter
Thursday, June 3, 2021
 
2

.& 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.

Wednesday, September 8, 2021
 
Vector
 
1

A lexical scope that can be closed over does not need to be mutable to be useful. Just consider curried functions as an example:

add = a -> b -> a+b
add1 = add(1)
add3 = add(3)
[add1(0), add1(2), add3(2), add3(5)] // [1, 2, 5, 8]

Here, the inner lamba closes over the value of a (or over the variable a, which doesn't make a difference because of immutability).

Closures are not ultimately necessary for functional programming, but local variables are not either. Still, they're both very good ideas. Closures allow for a very simple notation of the most(?) important task of functional programming: to dynamically create new functions with specialised behaviour from an abstracted code.

Tuesday, November 30, 2021
 
j3d
 
j3d
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :