言いたいことはそれだけか

KotlinとかAndroidとかが好きです。調べたことをメモします。٩( 'ω' )و

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は可愛い。

medium.com

上記のgenerateUrlFromString()Java正規表現からKotlinの正規表現に書き換えたコードがこちら。

    private fun generateUrlFromString(url: String): Url? =
        REGEX.toRegex().matchEntire(url)
            ?.destructured
            ?.let { (protocol, domain) ->
                Url(protocol, domain)
            }

これだけで十分可愛さが伝わる気がするが、蛇足ながら可愛いポイントを書いていく。

  1. 文字列REGEXtoRegex()Regexクラスに変換
  2. matchEntire()で引数url stringを渡すことによりMatchResultを取得
  3. 2で正規表現にマッチしなかった場合はnullになるので、?.destructuredを取得。これは正規表現にマッチしたgroupをDestructuredクラスで返してくれる
  4. 3ですでにnullの可能性があるので引き続き?.でletを呼ぶ。ポイントはここでラベルをつけることができるという点。サンプルコードだとprotocoldomainをすぐに使っているが、let blockのなかで複雑なことをしている場合にわざわざ名前をつけるために変数に代入しなくてもよくて可読性がグッと上がる。

これらをワインラインでスッキリかけるところがまた可愛い!

当然ながらKotlinのRegexJavaのMatcherとかをwrapしているので、めんどくさいところは全てやってくれていて最高。

言いたいことは以上です。