Mapの初期化でKeyをGStringにした場合、getメソッドを使用しないと値を取得できない。
表題の通り。
Mapの初期化時には変数名を直接指定できないので、GStringで対応していたらハマった。
- っというか、元々はcollectEntriesイジっててハマったのでcollectEntriesがおかしいって記事まで書いてた。。。
環境
$ groovy -version Groovy Version: 2.2.2 JVM: 1.8.0 Vendor: Oracle Corporation OS: Windows 7
- 2.3.0-beta-2でも発生しました。
現象
言葉で説明は下手くそなのでコードで。
def key1 = "a" // 変数は直接指定できない(文字列のKeyとして処理される) def map1 = [key1:"a"] assert map1 == ["key1":"a"] // GStringは別のKeyとして認識される def map2 = ["${key1}":"a"] assert map2 != ["a":"a"] assert map2 == ["${'a'}":"a"] assert map2.keySet()[0].class == org.codehaus.groovy.runtime.GStringImpl // StringとGStringの型 def key2 = "${key1}" assert key1.class == java.lang.String assert key2.class == org.codehaus.groovy.runtime.GStringImpl // containsKeyはGStringで格納されているとの事。 assert !map2.containsKey(key1) assert map2.containsKey(key2) // StringでもGStringでも取得できない assert map2[(key1)] == null assert map2[(key2)] == null assert map2.(key1) == null assert map2.(key2) == null // 直接getメソッドを呼ぶと取得が可能 assert map2.get("a") == null assert map2.get("${'a'}") == "a"
つまり、GStringを使用してMapを初期化するとStringではなくてGStringとして格納されるため、Stringで取得しようとしてもダメ。
では、GStringで指定しても[]の呼び出しは(getAtメソッド)Groovyがいい感じにStringに変換してくれているようなのでこれもダメ。
Javaメソッドのgetを直接呼ぶことでGStringをそのまま渡せるので習得ができます。
初期化以外の処理では、GStringはStringに変換されて格納されるため、格納時にも取得時にも問題は発生しません。
def key1 = "a" def key2 = "${key1}" // 初期化以外では変数はそのまま指定可能 // GStringもStringに変換されて格納される。 def map3 = [:] map3[key1] = "a" map3[key2] = "b" assert map3.size() == 1 assert map3[key1] == "b" assert map3[key2] == "b" assert map3.keySet()[0].class == java.lang.String assert map3.get(key1) == "b" assert map3.get(key2) == null
正直この挙動はどうなんでしょう。。。?
回避策
変数使いたいだけなら()を使用する事で回避可能。
文字列の結合が必要ならば「as String」でStringに変換する。
def key1 = "a" def map4 = [(key1):"a"] assert map4 == ["a":"a"] assert map4 != ["${'a'}":"a"] assert map4[key1] == "a" assert map4[key2] == "a" def map5 = [("${key1}_${key2}" as String):"a"] assert map5 == ["a_a":"a"] assert map5 != ["${'a_a'}":"a"] assert map5["a_a"] == "a" assert map5["${'a_a'}"] == "a"
参考
ここまで色々回り道して調べたんですが、下書き書いてからもう一回ググったらふもさん(id:fumokmm)のblogが出てきた。
5年前に調べてくれてるじゃないですかーやだー
しかも、id:uehajさんがJIRAまで調べてくれてるじゃないですかー
Groovyでマップのキーに文字列を指定する際の注意点 - No Programming, No Life
5年前でなおってないなら仕様ですね。
これわかりづらいと思うんですが。。。