fieldju
fieldju

Reputation: 1482

Lazy Map Values in Groovy

So in groovy you can define a string whos value is retrieved lazily each time its value is read.

System.setProperty('foo', 'bar')

def getMyValue = {
  return System.getProperty('foo')
}

def myData = [
  value1: "${ getMyValue() }",
  vaule2: "${ -> getMyValue() }",
]

System.setProperty('foo', 'bam')
println "${myData}"

System.setProperty('foo', 'baz')
println "${myData}"

When ran this yields

[value1:bar, vaule2:bam]
[value1:bar, vaule2:baz]

This is really nice for values that are Strings

What I would like to know if it is possible to do something similar for number types

def myData = [
   myInt: { -> Integer.valueOf(System.getProperty('somePropThatIsSetDownStream')) },
]

Or if there is way to override the get method on the map so that when get is performed I could execute a closure that transforms the value of myData.myInt

def valueTransformer = { key, map ->
    if(map[key] instanceOf Closure) {
      return map[key].call()
    }
    return map[key]
}

Is there away to maybe have a closure that overrides the logic that happens when myData.myInt is called?

Upvotes: 2

Views: 2233

Answers (1)

daggett
daggett

Reputation: 28564

it depends on syntax you want to achieve but there are could be a lot of solutions.

I suggest to implement Lazy closure class where toString will call the closure

@groovy.transform.CompileStatic
class Lazy extends Closure{
    @Delegate private Closure closure
    Lazy(Closure c){ 
        super(c.getOwner(), c.getThisObject())
        this.closure=c 
    }
    static Lazy of(Closure c){ new Lazy(c) }
    Object doCall(){ closure.call() }
    String toString(){ closure.call() as String }
}


def myData = [
   myInt: Lazy.of{ System.currentTimeMillis() },
]

//different printouts
println myData.myInt   //toString called
Thread.sleep(100)
println myData.myInt() //returns the value (not string)
Thread.sleep(100)
println myData

result:

1576168290790
1576168290901
[myInt:1576168291003]

Upvotes: 2

Related Questions