为什么Map不适用于Groovy中的GString?

Mic*_*das 4 groovy dictionary gstring

使用以下代码段我无法gString从地图中检索:

def contents = "contents"
def gString = "$contents"

def map = [(gString): true]

assert map.size() == 1 // Passes
assert gString.hashCode() == map.keySet().first().hashCode() // Passes, same hash code
assert map[gString] // Fails
Run Code Online (Sandbox Code Playgroud)

这怎么可能呢?

断言消息清楚地表明Groovy存在严重错误:

assert map[gString] // Fails
       |  ||
       |  |contents
       |  null
       [contents:true]
Run Code Online (Sandbox Code Playgroud)

这不是同一个问题,为什么groovy没有在字典中看到一些值? 第一个答案是:

您将GString实例添加为地图中的键,然后使用String实例搜索它们.

在这个问题中我明确添加GString并尝试检索GString.

也没有为什么在地图中处理GString键的方式有不同的行为?并且Groovy在GStringImpl上使用equals()和==的不同结果对我有一个答案.我不变异任何东西,我不混合String使用GString.

Bal*_*Rog 5

tl; dr:您似乎在Groovy的运行时参数重载评估中发现了一个错误.

回答:

map[gString]map.getAt(gString)通过Groovy的运算符重载机制直接评估运行时.到目前为止,这么好,但现在一切都开始出错了.Java LinkedHashMapgetAt在其类型层次结构中没有任何方法,因此Groovy必须使用动态关联的mixin方法(实际上该语句有点颠倒.在类层次结构中使用声明的方法之前, Groovy使用mixin方法.)

因此,简而言之,Groovy决定map.getAt(gString)使用类别方法DefaultGroovyMethods.getAt().轻松,对吧?除了这个方法有大量不同的参数重载,其中有几个可能适用,特别是考虑到Groovy的默认参数强制.

不幸的是,不是选择DefaultGroovyMethods.getAt(Map<K,V>,K),这似乎是一个完美的匹配,Groovy选择DefaultGroovyMethods.getAt(Object,String),强制GString关键参数成为一个String.由于实际密钥实际上是a GString,因此该方法最终无法找到该值.

对我来说,真正的杀手是,如果参数重载决策是直接从代码执行的(而不是在运算符解析和类别方法选择之后),那么Groovy会做出正确的重载选择!也就是说,如果你替换这个表达式:

map[gString]
Run Code Online (Sandbox Code Playgroud)

用这个表达式:

DefaultGroovyMethods.getAt(map,gString)
Run Code Online (Sandbox Code Playgroud)

然后正确解析参数重载,找到并返回正确的值.