Groovyでマップのキーに文字列を指定する際の注意点
はじめに
Groovyでは文字列を使用する際にGStringが使える。GStringのインスタンスは様々な場所でStringとして振舞ってくれるため、普段あまり意識しないのだが、マップのキーとして指定する場合、注意が必要だ。*1
def str = 'abc' assert str.class == java.lang.String assert "abc".class == java.lang.String assert "$str".class == org.codehaus.groovy.runtime.GStringImpl
$記号で式を埋め込むとGStringのインスタンスになるらしい。
ということで、動作確認
def s = 'S' def g = 'G' def gs = "$s" def gg = "$g" def map = ['S':'key is String'] def map2 = ["S":'key is String', "$g":'key is GString'] assert map.containsKey('S'), 'String直接指定で取得可' assert map.containsKey(s), 'String変数指定で取得可' assert !map.containsKey("${s}"), 'GStringでないので取得不可' assert !map.containsKey(gs), 'GStringでないので取得不可' assert map2.containsKey('S'), 'String直接指定で取得可' assert map2.containsKey(s), 'String変数指定で取得可' assert !map2.containsKey("${s}"), 'GStringでないので取得不可' assert !map2.containsKey(gs), 'GStringでないので取得不可' assert !map2.containsKey('G'), 'GStringでないので取得不可' assert !map2.containsKey(g), 'GStringでないので取得不可' assert map2.containsKey("${g}"), 'GString直接指定で取得可' assert map2.containsKey(gg), 'GString変数指定で取得可'
これを見るとStringと評価されたものがキーの場合、String。
GStringと評価されたものがキーの場合、GStringでないと取得できないようだ*2。GString→Stringへの自動変換は行ってくれないらしい。
なので、GStringの評価結果をStringのキーとしてマップで指定したい場合、以下のように強制変換すればひとまずOKかな?
def map3 = [("$g" as String):'key is String'] assert map3.containsKey('G'), 'String直接指定で取得可' assert map3.containsKey(g), 'String変数指定で取得可' assert !map3.containsKey("${g}"), 'GStringでないので取得不可' assert !map3.containsKey(gg), 'GStringでないので取得不可'
(動作確認はGroovy Version: 1.5.7 JVM: 1.6.0_10にて)