groovyclosuresexpando

groovy closure Expando call vs explicit


If I have a closure attached to an expando and the closure references a value on the expando like so...

def e = new Expando()
e.val = 'hi'
e.doit = { println delegate.val }
e.doit()

It works fine. and prints 'hi'

If I call the closure with the long form

e.doit.call()​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

It throws an error

groovy.lang.MissingPropertyException: No such property: val for class: Script1
at Script1$_run_closure1.doCall(Script1.groovy:4)
at Script1$_run_closure1.doCall(Script1.groovy)
at Script1.run(Script1.groovy:6)

This happens because the delegate is changed from e to the script. Why? I thought that e.doit() and e.doit.call() are supposed to be the same.

I can change the delegate manually - like so

def e = new Expando()
e.val = 'hi'
e.doit = { println delegate.val }
e.doit.delegate=e;
e.doit.call()​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​

Any ideas on how to skip the explicit setting of the delegate?


Solution

  • Yes, there is a better idea.

    Refer Expando instance directly (instead of delegate) when you know you are having a closure(dynamic method) defined for the Expando which is nothing but a dynamic bean. On the other hand, the same test would yield expected result when tested against a concrete class:

    def e = new Expando()
    e.val = 'hi'
    e.doit = {
        println delegate.class.name
        "$e.val Joseph"
    }
    
    assert e.doit() == 'hi Joseph'
    assert e.doit.call() == 'hi Joseph'
    
    class Test{
        String a
        def printA = {
            println delegate.class.name
            "$delegate.a Joseph"
        }
    }
    def test = new Test(a: 'hi')
    
    assert test.printA() == 'hi Joseph'
    assert test.printA.call() == 'hi Joseph'
    

    Note system out from println in both cases.