Почему карта не работает для GString в 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 map[gString] // Fails

Как на земле это возможно?

Сообщение подтверждения ясно показывает, что с Groovy что-то серьезно не так:

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

Это не тот же вопрос, что и почему Groovy не видит некоторые значения в словаре? Первый ответ там предполагает:

Вы добавляете экземпляры GString в качестве ключей на карту, а затем ищете их, используя экземпляры String.

В этом вопросе я четко добавляю GString и попытаться получить GString,

Также нет. Почему существуют разные способы обращения к ключам GString в картах? ни Groovy разные результаты использования equals() и == на GStringImpl есть ответ для меня. Я ничего не мутирую и не смешиваю String с GString,

2 ответа

Решение

tl;dr: Вы, похоже, обнаружили ошибку в оценке перегрузки аргументов Groovy во время выполнения.

Ответ:

map[gString] оценивается как map.getAt(gString) во время выполнения напрямую через механизм перегрузки оператора Groovy. Пока все хорошо, но сейчас все начинает идти не так, как надо. Ява LinkedHashMap класс не имеет getAt метод в любом месте иерархии типов, поэтому Groovy должен вместо этого использовать динамически связанные методы mixin (фактически этот оператор является своего рода обратным. Groovy использует методы mixin перед использованием объявленных методов в иерархии классов.)

Короче говоря, Groovy разрешает map.getAt(gString) использовать метод категории DefaultGroovyMethods.getAt(), Легко-peasy, верно? За исключением того, что этот метод имеет большое количество различных перегрузок аргументов, некоторые из которых могут применяться, особенно если принять во внимание приведение аргументов по умолчанию в Groovy.

К сожалению, вместо выбора DefaultGroovyMethods.getAt(Map<K,V>,K), который, казалось бы, идеально подходит, Groovy выбирает DefaultGroovyMethods.getAt(Object,String), который принуждает GString ключевой аргумент в String, Поскольку фактический ключ на самом деле GStringметод в конечном итоге не может найти значение.

Для меня настоящим убийцей является то, что если разрешение перегрузки аргумента выполняется непосредственно из кода (а не после разрешения оператора и выбора метода категории), то Groovy делает правильный выбор перегрузки! То есть, если вы замените это выражение:

map[gString]

с этим выражением:

DefaultGroovyMethods.getAt(map,gString)

тогда перегрузка аргумента разрешается правильно, и правильное значение найдено и возвращено.

С Groovy все в порядке. GString не является строкой. Он является изменяемым и поэтому никогда не должен использоваться в качестве ключа на карте (как любой другой изменяемый объект в Java).

Узнайте больше об этом в документации: http://docs.groovy-lang.org/latest/html/documentation/index.html

Другие вопросы по тегам