読者です 読者をやめる 読者になる 読者になる

No Programming, No Life

新しいNPNLです。http://d.hatena.ne.jp/fumokmm/ から引っ越してきました。

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にて)

*1:うっかりマップのキーにGStringを指定してしまい、Stringで値を取得しようとして、取得できないという罠に嵌った。

*2:厳密にGStringとStringを区別しているみたい。