Kotlinの拡張機能で冗長な〇〇Utilsから脱却する

BPSの福岡拠点として一緒にお仕事させていただいています、株式会社ウイングドアの坂本です。
気がつけばこの挨拶も5回目となります。

私はPHP、Rubyなどでweb系の案件を担当していて、最近はKotlinでAndroidアプリの開発をしているのですが、
Kotlinの拡張機能が便利で面白いと思ったのでご紹介したいと思います。

拡張機能とは

よく「文字列や数値などに対して繰り返し使う便利メソッドを実装したい!」ということがありますよね。
(日付の表示の指定、表示文字列の特殊な変換、コレクションに特殊なフィルターをかける、etc.)

PHPなどで実装している場合、StringUtilsクラスなどを作成して、それで呼び出したりするのですが、
どうしても冗長になってしまい「なんとかしたいな」と思うこともあります。

そんな時に使えるのが拡張機能!
既存のクラスに対し、関数を追加で定義できるとても便利な機能です。
StringやIntなど、誰でもお世話になるクラスにも定義できますし、
これを利用するとUtilsクラスを利用するよりも分かりやすく書くことができます。

  • Utilsクラスを作成した場合
// Java
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))
  • 拡張機能を利用した場合
// Java
list.swap(list.binarySearch(otherList.max()), list.max())

拡張機能を利用するとこんなにシンプルに書けるようになります。

※ コードは「Kotlin のリファレンス 拡張 (extension) 」「動機」の項より引用

実装の仕方

拡張機能の実装は非常に簡単です。
試しに、文字列を置換する機能を実装してみましょう。

val str = "りんごばななりんご"

fun String.replaceBanana(): String {
    return this.replace("ばなな", "りんご")
}

println(str) // -> "りんごばななりんご"
println(str.replaceBanana()) // -> "りんごりんごりんご"

拡張機能の定義は、直前でも、メソッドの中でも、このように文字列を定義したあとでも可能です。
thisが代入した値となり、これを変換して値を返しています。

関数だけでなくプロパティを拡張できたり、Nullも許容することもできたりと、かなりできることは多いです。
詳細はこちらに記載されているのでぜひご覧ください。

で、どこに記述しよう?

上記で説明したように拡張機能は記述場所を選びません。
ですが実際にプロジェクトで繰り返し使う場合はできるだけ共通の場所に記載したいですよね。

かといって、仮にStringやIntなど、元々定義されているクラスに常に拡張している状態だと、後々どのような影響がでるか分かりません。
できるだけ使う時、使う箇所だけで拡張できるようにしたいです。

そんな時はインターフェース!

1. 分かりやすい名前でインターフェースを作成し、先程のメソッドを記載します。

interface StringInterface {
    fun String.replaceBanana(): String {
        return this.replace("ばなな", "りんご")
    }
}

2. 利用するクラスで1.のインターフェースを実装します。

これでOutputクラス内ではString型の変数に対してreplaceBananaメソッドが利用できます。
(個人的に「実装します」といいつつ関数の中身は記載しなくて良いのは変な感じがします)

class Output: StringInterface{
    fun print() {
    var str = "りんごばななりんご"
    println(str.replaceBanana()) 
    }
}

3. 確認。試しに以下のコードを実行してみるとOutputクラス内ではreplaceBananaが利用でき、それ以外の場所ではエラーが発生するのが分かると思います。

interface StringInterface {
    fun String.replaceBanana(): String {
        return this.replace("ばなな", "りんご")
    }
}

class Output: StringInterface{
    fun print() {
    var str = "りんごばななりんご"
    println(str.replaceBanana()) 
    }
}


fun main(args: Array<String>) {
    val output = Output()
    output.print() // -> 「りんごりんごりんご」と表示

    // var str = "りんごばななりんご"
    // str.replaceBanana() // コメントアウトを解除するとエラーが発生する
}

まとめ

Kotlinを使ってみてまだ日は浅いですが、この拡張機能やNULL安全など、便利な機能も多く触っていて楽しい!
まだまだ活用できておらず、知らない機能も多いのですが、上手く使って効率的な開発をしていきたいです。


株式会社ウイングドアでは、Ruby on RailsやPHPを活用したwebサービス、webサイト制作を中心に、
スマホアプリや業務系システムなど様々なシステム開発を承っています。

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

wingdoor

株式会社ウイングドアは福岡のシステム開発会社です。Ruby on RailsやPHPを活用したwebサービス、webサイト制作を中心に、 スマホアプリや業務系システムなど様々なシステム開発を承っています。 中途採用を絶賛募集しています! https://wingdoor.co.jp/

wingdoorの書いた記事

BPSアドベントカレンダー

週刊Railsウォッチ