Search code examples
groovyclosures

groovy : how to modfiy value in closure ( similar with shell pipe )


to simplify the code, I tried to using pipe-similar usage like below:

  • foo( 'abc' ) -> 'abc ~> 123' ( default result )
  • foo( 'abc' ).bar( 'efg' ) -> 'efg : abc ~> 123' ( additional if necessary )

So, I tried to using Map + Closure to handle as below:

def foo ( String aStr ) {
    String constant = '123'
    String result = "${aStr} ~> ${constant}"

    [
        bar : { String bStr -> result = "${bStr} : ${result}"; return result }
    ]
    println result
}

foo( 'abc' )                    // abc ~> 123
foo( 'abc' ).bar( 'efg' )       // java.lang.NullPointerException: Cannot invoke method bar() on null object

second try

I was added default key in Map structure as below. It works well, however :

  • call println twice
  • additional default key-value has to be added
def foo ( String aStr ) {
    String constant = '123'
    String result = "${aStr} ~> ${constant}"
    
    [
        bar     : { String bStr -> result = "${bStr} : ${result}"; println "${result}" } ,
        default : { println result }
    ]
}

foo( 'abc' ).bar( 'efg' )          // efg : abc ~> 123
foo( 'abc' ).default()             // abc ~> 123

third try

make bar allows non-parameter, so it can be call as foo(..).bar(), but stills cannot be invoking via foo(..) directly

def foo ( String aStr ) {
    String constant = '123'
    String result = "${aStr} ~> ${constant}"

    [
        bar : { String bStr = '' -> result = "${bStr ? "${bStr} : " : ''}${result}"; return result }
    ]
}

foo( 'abc' )                 // [bar:ConsoleScript22$_foo_closure1@5e7667fd]
foo( 'abc' ).bar()           // abc ~> 123
foo( 'abc' ).bar( 'efg' )    // efg : abc ~> 123

is there any better solutions?


Solution

  • Not 100% sure I grasp the question, but you could write a helper class which generates a chain of elements

    class Result {
        String name
        def value
    
        def bar(String aStr) {
            new Result(name: aStr, value: this)
        }
    
        @Override
        String toString() {
            if (value instanceof String) {
                return "${name} ~> ${value}"
            } else {
                return "${name} : ${value?.toString()}"
            }
        }
    }
    

    Then, your Foo method becomes:

    Result foo(String aStr) {
        new Result(name: aStr, value: '123')
    }
    

    And calling the following, prints the following results:

    println foo('abc')
    println foo('abc').bar('efg')
    println foo('abc').bar('efg').bar('hahaha')
    
    abc ~> 123
    efg : abc ~> 123
    hahaha : efg : abc ~> 123