プログラマに優しい現実指向JVM言語 Kotlin入門

第6回 KotlinでAndroidプログラミング

この記事を読むのに必要な時間:およそ 7 分

Kotlin活用のアイデア

KotlinでAndroid開発を始めると,すぐにその快適さに気づくと思います。ここではその快適さをもたらしてくれるKotlin活用のヒントを紹介します。

関数リテラルでイベントリスナ

KotlinはJavaとの相互運用性が高いです。JavaコードをKotlinで呼び出すのがすごく簡単です。

ViewクラスのメソッドsetOnClick Listener(View.OnClickListener)の例を考えます。ビューのクリック時のアクションを登録するこのメソッドですが,クリック時のアクションはView.OnClickListenerというインターフェースで表現されています。このインターフェースはfun onClick(v: View): Unitというメソッドを1つだけ持ちます。このように抽象メソッドをただ1つ持ったインターフェースを引数に取るメソッドに,Kotlinコードでは関数を渡すことができます! この例では(View) -> Unit型の関数をView#setOnClickListener(View.OnClickListener)の引数として渡せるということです。

リスト2ではクリック時のアクションを関数リテラルで表現してリスナ登録しています。参考までにJavaで書き直したものをリスト3に示します。

リスト2 OnClickListenerの代わりに関数を渡せる

button.setOnClickListener {
  Log.d(TAG, "Clicked")
}

リスト3 Javaで書くとノイズが多い

// Javaコード
button.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    Log.d(TAG, "Clicked");
  }
});

拡張関数で便利APIを作る

既存の型にメソッドを生やせる拡張関数がすごく便利なことは第5回で解説しました。この拡張関数を駆使してAndroid標準のAPIを使いやすく改造してみましょう。

まずはシンプルな例としてトーストを挙げます。リスト4のようなコードを書きました。この拡張関数toastにより,Contextのサブクラス内,たとえばActivity内でトースト表示する際にはtoast("こんにちは")と記述するだけで済みます。このような記述を得るためだけにBaseActivityのようなスーパークラスを導入していまいがちですが,いろいろ厄介なBaseActivityを避けられます。

リスト4 Contextの拡張関数としてトースト表示機能を追加

fun Context.toast(msg: String) {
  Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}

もう1つ面白い例を紹介します。リスト5では,プリファレンスの編集を便利にする拡張関数を定義しました。

リスト5 プリファレンスの編集を便利に

fun SharedPreferences.edit(f: SharedPreferences.Editor.() -> Unit) {
    val editor = this.edit()
    editor.f()
    editor.apply()
}

platformName("putLong")
fun SharedPreferences.Editor.put(pair: Pair<String, Long>) {
    putLong(pair.first, pair.second)
}

platformName("putString")
fun SharedPreferences.Editor.put(pair: Pair<String, String>) {
    putString(pair.first, pair.second)
}

呼び出し側のコードはどのようになるか想像できますか? すでに取得しているプリファレンスprefに対してリスト6のようなコードが記述できるようになります。

リスト6  プリファレンス編集が簡単になった

pref edit {
  put("id" to 123L)
  put("name" to "たろう")
}

まずリスト5の拡張関数editを見てください。この拡張関数の役目は,プリファレンスの編集が終わったあとにShared Preferences.Editor#applyを実行することです。引数として受け取る関数fの中で実際に編集が行われ,fの呼び出しのあとにapplyを呼び出しているだけです。引数fの型に注目してください。初めて登場する記法です。SharedPreference.Editor.() -> Unitという関数型です。() -> Unitだけであれば「引数を取らずに何も返さない関数の型」と読めるのは,すでにご存じかもしれません。頭に付くSharedPreference.Editor.は,メソッドのレシーバとみなせます。つまりfは,クラスSharedPreference.Editorの() -> Unitなメソッドなのです!

このfは単なる関数ではなく,メソッドですので単体では呼び出せません。レシーバとなるオブジェクトが必要です。リスト5でeditor.f()のように呼び出しているのはそのためです。editorがレシーバとなりfを呼び出しています。

このような拡張関数,いや拡張メソッドは何の役に立つのでしょうか。実際,fの型を(SharedPreference.Editor) -> Unitとして,f(editor)と呼び出せば機能としては同じことができます。しかし呼び出し側(リスト6)のコードが変わってきます。it.put("id" to 123L)のようにレシーバを(それが明らかなのにもかかわらず)明示する必要が出てきます。fSharedPreference.Editorの拡張メソッドであることでput("id" to 123L)のようにレシーバを省略することが可能になります。

それからリスト5の2つの拡張関数putについてです。この2つの関数は,Kotlin標準ライブラリに含まれるクラスPairを引数に取ります(説明のためにわざとらしくそうしています)。クラスPairはその名のとおり組,ペアを表現するクラスです。今回の場合,プリファレンスとして保存する対象となっているのでPairの第1要素をキー,第2要素を値としています。2つの関数putは同名ですが,Pair<String,StringPair<String, Long>で引数の型が異なります。Kotlinではこれを区別できますが,Javaではできません。そのためJava用に別名を付けてやる必要があります。platformNameアノテーションを付けて,その引数にJava用の名前を付けるだけです。

最後にリスト6を見てください。putの引数を"id" to 123Lと記述しています。これはPairリテラルです。と言うとわかりやすいですが少し違います。toは任意の型に対する拡張関数で,Pairインスタンスを生成します。"id" to 123L"id".to(123L)ともPair("id", 123L)とも記述できます。

著者プロフィール

長澤太郎(ながさわたろう)

早稲田大学情報理工学科を2012年に卒業。同年入社したメーカー系SIerを経て,2013年にエムスリー株式会社へ入社。以来,世界の医療を変革するためソフトウェアエンジニアとして従事。

日本Kotlinユーザグループ代表,日本Javaユーザグループ幹事を務める。国内初となるKotlin入門書「Kotlinスタートブック」(リックテレコム)の著者。

ビールとディズニーが大好き。

コメント

コメントの記入