Reputation: 424
I need null-safe access to map/property by dynamic name of the key. Map can be null itself. I've came up with map?.(keyName)
but wonder why it works not as expected and returns always null
def map = [key: 'value']
def keyName = 'key'
map.each({ key, value -> println("[${key.class}]: [${value.class}]")})
println("keyName class is [${keyName.class}]")
println(map?.(keyName)) // null <--------- My question
// Alternatives that work well
println(map?.(keyName as String)) // value
println(map?.(keyName as java.lang.String)) // value
println(map?.get(keyName)) // value
Output
[class java.lang.String]: [class java.lang.String]
keyName class is [class java.lang.String]
null
value
value
value
If both keyName
and map keys are of type java.lang.String
why map?.(keyName)
does not return value? Why map?.(keyName as String)
returns?
Checked in Groovy web console
Upvotes: 3
Views: 4883
Reputation: 1539
As @Vampire pointed out
The dot-notation for retrieval only works for String keys
but in my case I had a need to set key dynamically and it was not string key. So solution I end up was to iterate through out the map until I reach my key section:
def SetMapEntry(String key, String value, Map map) {
def entry = map
def keyRoute = key.split('\\.')
def entryLength = keyRoute.size()
key.split('\\.').eachWithIndex{it,index->
if (entryLength - 1 == index) {
entry.(it as String) = value
}
else {
entry = entry?.get(it)
}
}
}
Upvotes: 0
Reputation: 38639
The parenthese just control precedence of evaluation. With (keyName)
, there is nothing to evaluate in the parentheses, so the parentheses are meaningless and map?.(keyName)
is identical to map?.keyName
which means map?.get('keyName')
.
With map?.(keyName as String)
there is something to evaluate in the parentheses, so it evaluated first and is resolved to map?.'key'
which means map?.get('key')
.
If you want to use a dynamic key value, you have to make sure it is evaluated by some expression. Various choices (some you already found) are:
map?.(keyName as String)
map?.get(keyName)
map?."$keyName"
map[keyName]
While the last option is not null
-safer regarding the map of course. I personally prefer the third method.
You might wonder why def map = [(keyName): 'value']
works for using the value of keyName
as key, but map?.(keyName)
does not work for using the value of keyName
for retrieval, but the reason is simple. The dot-notation for retrieval only works for String keys. The defining of the map with the parentheses works for any object type. So you can use any Object as key when defining a map with key in parentheses, but you can only ever extract objects from a map with String key with the dot-notation, hence it is ok that the semantics are different here regarding the parentheses.
Upvotes: 6
Reputation: 14519
Groovy is resolving map?.(keyName)
to map?.get('keyName')
. Using map?.keyName
yields the same result:
def map = [key: 'value', keyName: 'gotcha']
def keyName = 'key'
result = map?.(keyName)
assert result == 'gotcha'
assert map?.keyName == 'gotcha'
Upvotes: 4