为什么我无法获得与 Groovy 中映射的完全相同的 GString?

Why I cannot get exactly the same GString as was put to map in Groovy?

使用以下代码片段我无法从地图中检索 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 gString.is(map.keySet().first()) // Passes, exactly the same object
assert map[gString] // Fails

这怎么可能?

这里有趣的是 map.get(map.keySet()[0]) 工作正常,而 map.get[map.keySet()[0]] 不工作。

断言消息清楚地表明有问题:

assert map[gString] // Fails
       |  ||
       |  |contents
       |  null
       [contents:true]

这不是同一个问题 Why groovy does not see some values in dictionary? 第一个答案建议:

You're adding GString instances as keys in your map, then searching for them using String instances.

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

同样Why are there different behaviors for the ways of addressing GString keys in maps? nor Groovy different results on using equals() and == on a GStringImpl have an answer for me. I do not mutate anything and I do not mix String with GString. Groovy documentation也没有帮助。

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

答案:

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

因此,长话短说,Groovy 解析 map.getAt(gString) 使用类别方法 DefaultGroovyMethods.getAt()。简单易行,对吧?除了此方法有大量不同的参数重载,其中一些可能适用,尤其是当您考虑 Groovy 的默认参数强制时。

不幸的是,Groovy 没有选择似乎是完美匹配的 DefaultGroovyMethods.getAt(Map<K,V>,K),而是选择了 DefaultGroovyMethods.getAt(Object,String),它将 GString 关键参数强制转换为 String。由于实际的键实际上是一个GString,该方法最终无法找到该值。

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

map[gString]

用这个表达式:

DefaultGroovyMethods.getAt(map,gString)

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