3

使用以下代码段,我无法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]

这与为什么 groovy 看不到字典中的某些值不是同一个问题? 那里的第一个答案表明:

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

在这个问题中,我清楚地添加GString并尝试检索GString.

也不是为什么在地图中处理 GString 键的方式有不同的行为?在GStringImpl上使用 equals() 和 == 的 Groovy 不同的结果对我来说也有答案。我不会改变任何东西,也不会StringGString. Groovy 文档也没有帮助。

4

1 回答 1

3

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

回答:

map[gString]map.getAt(gString)通过 Groovy 的运算符重载机制直接在运行时进行评估。到目前为止,一切都很好,但现在一切都开始出错了。JavaLinkedHashMap类在其类型层次结构中的任何地方都没有getAt方法,因此 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]

用这个表达式:

DefaultGroovyMethods.getAt(map,gString)

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

于 2016-08-25T20:13:23.260 回答