Groovy String evaluation runtime

2020-07-23 07:19发布

问题:

Hi I have a String value that is populated runtime and I want to use it to construct another String.

static value= ''
static construct = "${-> value - '/'}"

So when I value = "/www.stackoverflow.com" , construct is equal to "www.stackoverflow.com"

but when I do

static value= ''
static construct = {-> value - '/'}

construct is equals to some closure name. I am wondering what is the purpose of this? Why using closure,GString everything is ok? And why when use only closure nothing happens?

Edited: The value is changed from this method call.

def someCoplmexMethod(){
   value="/www.stackoverflow.com"
}

回答1:

In short "${-> value - '/'}" is a fully lazy evaluated GString for value.

First to clarify some things... "value" is a String with the value "value". "$value" is a GString equal to the value of value as String. So if value is "lol.com", then this will be the value the GString is equal to as well.

Important to notice are two things here, the value of the GString is evaluated lazy, but the objects used for this are not. So if in "$value" value is a class with a toString() method, that will return a different String each time, then the GString's toString() would be different each time. Of course that is not the case for String objects. So if you for example have code like

def value=1
def gstring="$value"
value=2
assert gstring=="1"

then the gstring will still be "2".

{->value} is now a open block, that will return value. The evaluation is lazy and thus each time. So in

def value=1
def closure={->value}
def a=closure()
value=2
def b=closure()
assert a==1
assert b==2

then a will get the value 1 and b the value 2. The method-call-like expression closure() will call the open block.

Finally "${-> value - '/'}" is to be seen as a GString containing an open block. The GString will evaluate the open block each time it's toString() method is called like usual, but this time this will result in the call of the open block and thus evaluate value itself again. The result is:

def value=1
def gstring="${-> value}"
value=2
assert gstring=="2"

If you compare the example with the simple GString version, then you will see that the result changed. A more complex example:

def i=1
def gstring="${-> i++}"
assert gstring=="1"
assert i==2
assert gstring=="2"
assert i==3
assert gstring=="3"
assert i==4

As you can see, the toString()-value of gstring changes every time it is evaluated, because of the open block.