KotlinのRegexとDestructuredで文字列からdata classに変換する
Androidでアプリを書いていて、正規表現を扱うときにjava.util.regex.Matcher
を使うこと多いが、あれは個人的には好きではない。もっとスッキリかけるんじゃないかなぁといつも思ってしまう。
例えば、URLからprotocolとdomainを正規表現を使って取り出すコードを書こうと思うとこんな感じになるかと思う。
companion object { private const val REGEX: String = "(.*)://(.*)" } fun hoge() { val urlString = "http://www.com" val url = generateUrlFromString(urlString) } private fun generateUrlFromString(url: String): Url? { val matcher = Pattern.compile(REGEX).matcher(url) return if (matcher.find()) { val protocol = matcher.group(1) val domain = matcher.group(2) return Url(protocol, domain) } else { null } } data class Url(val protocol: String, val domain: String)
再帰の場合に注意が必要で、上記の場合だとmatcher.group()
が0ではなく1と2だったり何かとやらかしてしまったりする。(matcher.group(0)
にはhttp://www.com
が入ってくる)
そんなときに、この記事見て最高では????と思ったので自分でも試してみることにした。一行でまとめると Destructured
クラスが最高では?という話です。やっぱりKotlinは可愛い。
上記のgenerateUrlFromString()
をJavaの正規表現からKotlinの正規表現に書き換えたコードがこちら。
private fun generateUrlFromString(url: String): Url? = REGEX.toRegex().matchEntire(url) ?.destructured ?.let { (protocol, domain) -> Url(protocol, domain) }
これだけで十分可愛さが伝わる気がするが、蛇足ながら可愛いポイントを書いていく。
- 文字列REGEXをtoRegex() でRegexクラスに変換
- matchEntire()で引数url stringを渡すことによりMatchResultを取得
- 2で正規表現にマッチしなかった場合はnullになるので、
?.
でdestructuredを取得。これは正規表現にマッチしたgroupをDestructured
クラスで返してくれる - 3ですでにnullの可能性があるので引き続き
?.
でletを呼ぶ。ポイントはここでラベルをつけることができるという点。サンプルコードだとprotocol
とdomain
をすぐに使っているが、let blockのなかで複雑なことをしている場合にわざわざ名前をつけるために変数に代入しなくてもよくて可読性がグッと上がる。
これらをワインラインでスッキリかけるところがまた可愛い!
当然ながらKotlinのRegexもJavaのMatcherとかをwrapしているので、めんどくさいところは全てやってくれていて最高。
言いたいことは以上です。