我需要通过密钥的动态名称对映射/属性进行空安全访问。 Map本身可以为null。我想出了map?.(keyName)
,但想知道为什么它的效果不如预期,并始终返回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
输出
[class java.lang.String]: [class java.lang.String]
keyName class is [class java.lang.String]
null
value
value
value
如果keyName
和地图键的类型都为java.lang.String
,为什么map?.(keyName)
不会返回值?为什么map?.(keyName as String)
会返回?
答案 0 :(得分:5)
括号只是控制评估的优先级。使用(keyName)
时,括号中无法评估任何内容,因此括号无意义,map?.(keyName)
与map?.keyName
相同,这意味着map?.get('keyName')
。
使用map?.(keyName as String)
时,要在括号中进行评估,因此首先进行评估并将其解析为map?.'key'
,这意味着map?.get('key')
。
如果要使用动态键值,则必须确保通过某个表达式对其进行评估。各种选择(你已经发现的一些)是:
map?.(keyName as String)
map?.get(keyName)
map?."$keyName"
map[keyName]
虽然最后一个选项不是null
- 当然对地图更安全。我个人更喜欢第三种方法。
您可能想知道为什么def map = [(keyName): 'value']
可以使用keyName
的值作为键,但map?.(keyName)
不适用于使用keyName
的值进行检索,但是原因很简单。检索的点符号仅适用于String键。使用括号定义地图适用于任何对象类型。因此,在使用括号中的键定义地图时,您可以使用任何Object作为键,但是您只能使用带有点符号的String键从地图中提取对象,因此这里的语义与括号的语义不同。
答案 1 :(得分:3)
Groovy正在将map?.(keyName)
解析为map?.get('keyName')
。使用map?.keyName
会产生相同的结果:
def map = [key: 'value', keyName: 'gotcha']
def keyName = 'key'
result = map?.(keyName)
assert result == 'gotcha'
assert map?.keyName == 'gotcha'