Por que existem comportamentos diferentes para as maneiras de abordar as chaves GString nos mapas?

Enquanto estudava a sintaxe Groovy (2.4.4) na documentação oficial, deparei-me com o comportamento especial sobre mapas com GStrings como identificadores. Conforme descrito na documentação, GStrings são uma má idéia como identificadores de mapa (hash), porque os códigos de hash de um objeto GString não avaliado diferem de um objeto String comum com a mesma representação que o GString avaliado.

Exemplo:

def key = "id"
def m = ["${key}": "value for ${key}"]

println "id".hashCode() // prints "3355"
println "${key}".hashCode() // prints "3392", different hashcode

assert m["id"] == null // evaluates true

No entanto, minha expectativa intuitiva era de que o uso do identificador GString real para endereçar uma chave no mapa realmente fornecerá o valor - mas não o faz.

def key = "id"
def m = ["${key}": "value for ${key}"]

assert m["${key}"] == null // evaluates also true, not expected

Isso me deixou curioso. Então, eu tive várias sugestões sobre esse problema e fiz alguns experimentos.

(lembre-se de que sou novato no Groovy e estava apenas fazendo um brainstorming em andamento - continue na Sugestão 4, se você não quiser ler como tentei examinar a causa do problema)

Sugestão # 1. O hashcode para objetos GString funciona / é implementado de maneira não determinística por qualquer motivo e oferece resultados diferentes, dependendo do contexto ou do objeto real.

Isso acabou sendo um absurdo muito rápido:

println "${key}".hashCode() // prints "3392"
// do sth else
println "${key}".hashCode() // still "3392"

Sugestão # 2. A chave real no mapa ou o item do mapa não possui a representação ou código de hash esperado.

Olhei mais de perto o item no mapa, a chave e seu código de hash.

println m // prints "[id:value for id]", as expected
m.each { 
    it -> println key.hashCode() 
} // prints "3355" - hashcode of the String "id"

Portanto, o código de hash da chave dentro do mapa é diferente do código de hash do GString. HA! ou não. Embora seja bom saber, na verdade não é relevante porque ainda conheço os códigos de hash reais no índice do mapa. Acabei de refazer uma chave que foi transformada em uma sequência de caracteres depois de inserida no índice. Então, o que mais?

Sugestão # 3. O método de igualdade de um GString tem um comportamento desconhecido ou não implementado.

Não importa se dois códigos de hash são iguais, eles podem não representar o mesmo objeto em um mapa. Isso depende da implementação do método equals para a classe do objeto-chave. Se, por exemplo, o método equals não for implementado, dois objetos não serão iguais, mesmo que o código hash seja idêntico e, portanto, a chave do mapa desejada não possa ser endereçada corretamente. Então eu tentei:

def a = "${key}"
def b = "${key}"

assert a.equals(b)  // returns true (unfortunate but expected)

Portanto, duas representações da mesma GString são iguais por padrão.

Eu pulo algumas outras idéias que tentei e continuo com a última coisa que tentei antes de escrever este post.

Sugestão # 4. A sintaxe do acesso é importante.

Isso foi um verdadeiro matador de entendimento. Eu sabia antes: existem maneiras sintaticamente diferentes de dois valores de mapa de acesso. Cada caminho tem suas restrições, mas achei que os resultados permaneciam os mesmos. Bem, isso surgiu:

def key = "id"
def m = ["${key}": "value for ${key}"]

assert m["id"] == null // as before
assert m["${key}"] == null // as before
assert m.get("${key}") == null // assertion fails, value returned

Portanto, se eu usar o método get de um mapa, obtenho o valor real da maneira que esperava em primeiro lugar.

Qual é a explicação para esse comportamento de acesso ao mapa em relação ao GStrings? (ou que tipo de erro de novato está oculto aqui?)

Obrigado pela sua paciência.

EDITAR: Receio que minha pergunta real não esteja claramente definida, então, aqui está o caso, resumido e conciso:

Quando eu tenho um mapa com um GString como uma chave como esta

def m = ["${key}": "value for ${key}"]

por que isso retorna o valor

println m.get("${key}")

mas isso não

println m["${key}"]

?

questionAnswers(1)

yourAnswerToTheQuestion