Chaînes Groovy vs Chaînes Java


J'ai une chaîne en groovy que je fais à partir d'autres chaînes, exemple:

def final PREFIX = "myprefix"
def prefix2 = PREFIX + ".whatever1"

Supposons maintenant que j'ai un HashMap et que je veux faire une recherche en utilisant {[5] } dans le cadre d'une clé:

HashMap<String,String> map = new HashMap<String,String>()
map.put("myprefix.whatever1.value","aaa")

Si je le fais:

def key = "${prefix2}.value"
String result=(map.get(key))

Alors result = null, mais si je le fais:

String key="${prefix2}.value"
String result=(map.get(key))

, Puis result = aaa.

Je peux comprendre pourquoi cela se produit, évidemment un problème d'inférence de type. Mais je trouve toujours que cela me fait me sentir icky. Quelque chose ne "se sent pas bien" à ce sujet. Savez-vous ce que je veux dire?

Est ce type de chose normale et devrait être "attendu"? Est-ce que je demande trop pour Groovy de savoir que si j'ai créé la chaîne en utilisant des guillemets, cela devrait fonctionner lorsqu'il est utilisé pour rechercher une valeur dans un Hashmap <String, String> sans être défini comme une référence d'objet String? Est-ce un bug ou une fonctionnalité?

Author: iloveretards, 2016-08-19

2 answers

Il y a plusieurs choses qui doivent être prises en considération pourquoi cela se produit.

Lorsque vous créez une variable HashMap<String,String> map, vous vous attendez à ce que, comme en java, vous ne puissiez y ajouter que des chaînes en tant que clés et valeurs. Ce n'est pas le cas dans groovy car les arguments de type ne seront pas pris en compte, ce qui signifie que ce qui suit fonctionnera:

HashMap<String,String> map = new HashMap<String,String>()
map.put(1, 2)   
assert map.get(1) == 2
assert map.get(1) instance of Integer

Comme vous l'avez indiqué, une chaîne interpolée comme "${prefix2}.value" est une instance de GString (GStringImpl pour être précis) donc le ce qui suit est vrai

def key = "${prefix2}.value"
assert key instanceof GString

Donc, map.get(key) sera invoqué avec un paramètre GString sans erreur puisque la contrainte de chaîne est supprimée, ce qui ne serait pas un problème si le GStringImpl donnerait le même hashcode que le String, mais ce n'est pas le cas

assert key == "myprefix.whatever1.value"
assert key.hashCode() != "myprefix.whatever1.value".hashCode()

C'est pourquoi le get sur la carte renvoie un null

La façon de contourner cela est, comme vous l'avez indiqué en utilisant toString() ou en affectant la GString à une variable String (toString() est appelé implicitement)

Une autre façon, ce que je préférerais, c'est de ne pas mélanger l'accès à la carte de style java habituel avec celui groovy. Si vous utilisez la notation de propriété ou la clé indexée sur la carte à l'aide de la GString, cela fonctionnera parfaitement:

def final PREFIX = "myprefix"
def prefix2 = PREFIX + ".whatever1"

// def map = [:] // shorter will be a LinkedHashMap
HashMap<String,String> map = [:] as HashMap // if you really need HashMap
map."myprefix.whatever1.value" = "aaa"

def key = "${prefix2}.value"

assert map[key] == "aaa"
assert map."${prefix2}.value" == "aaa"
assert map."$key" == "aaa"
 2
Author: Gergely Toth, 2016-08-21 16:22:50

Vous pouvez appeler la méthode toString() de GString (chaîne interpolée dans groovy), qui renverra un java.lang.String, donc si vous voulez utiliser une telle chaîne pour récupérer une valeur à partir d'un HashMap, par exemple, vous l'utiliseriez comme ceci:

 def prefix = "some.string"
 map.get("${prefix}.value".toString())
 0
Author: iloveretards, 2016-08-20 12:54:54