MainActivity.kt
をリスト1 のように編集して、ビルド、実行してみてください。KotlinコードがAndroid上で動いていることが確認できます。
リスト1 Hello, Kotlin!
public class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
"Kotlin".hello()
}
fun String.hello() {
val msg = "Hello, $this!"
Toast.makeText(this@MainActivity, msg, Toast.
LENGTH_SHORT).show()
}
}
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)
のようにレシーバを(それが明らかなのにもかかわらず)明示する必要が出てきます。f
がSharedPreference.Editor
の拡張メソッドであることでput("id" to 123L)
のようにレシーバを省略することが可能になります。
それからリスト5の2つの拡張関数put
についてです。この2つの関数は、Kotlin標準ライブラリに含まれるクラスPair
を引数に取ります(説明のためにわざとらしくそうしています)。クラスPair
はその名のとおり組、ペアを表現するクラスです。今回の場合、プリファレンスとして保存する対象となっているのでPair
の第1要素をキー、第2要素を値としています。2つの関数put
は同名ですが、Pair<String,String
とPair<String, Long>
で引数の型が異なります。Kotlinではこれを区別できますが、Javaではできません。そのためJava用に別名を付けてやる必要があります。platformName
アノテーションを付けて、その引数にJava用の名前を付けるだけです。
最後にリスト6を見てください。put
の引数を"id" to 123L
と記述しています。これはPair
リテラルです。と言うとわかりやすいですが少し違います。to
は任意の型に対する拡張関数で、Pair
インスタンスを生成します。"id" to 123L
は"id".to(123L)
ともPair("id", 123L)
とも記述できます。
ライブラリ・ツール
KotlinでのAndroid開発をより快適にしてくれるライブラリやツールを紹介します。
Kotter Knife
Kotter Knife はView Injectionライブラリです。頻繁に登場するfindViewById
によるビューのマッピング作業から解放してくれるライブラリです。Java用のButter Knife のKotlin版と言えます。ちなみに開発者はAndroid界隈で名高い(どころかスーパースターの)Jake Whartonさんです。
導入は簡単です。リスト7 のようにbuild.gradleを編集してください。
リスト7 Kotter Knife導入
dependencies {
// (略)
compile 'com.jakewharton:kotterknife:0.1.0-SNAPSHOT'
}
repositories {
// (略)
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots/'
}
}
導入後、すぐに使い始められます。リスト8 にKotter Knifeの簡単な使用例を示します。MainActivity
のプロパティとして各ビュー(nameEditText
とsubmitButton)
を保持しています。onCreate
メソッド内でfindByViewId
メソッドを呼び出してビューのマッピングを行うのが通常の方法ですが、このコードにはfindByViewId
が登場しません。setContent View(R.layout.activity_main)
を呼び出した後すぐにsubmitButton
に対してクリックリスナを登録しています。
リスト8 Kotter Knife使用例
class MainActivity : Activity() {
val nameEditText: EditText by bindView(R.id.name_edit_text)
val submitButton: Button by bindView(R.id.submit_button)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
submitButton.setOnClickListener {
val name = nameEditText.getText().toString()
Toast.makeText(this, name, Toast.LENGTH_SHORT).show()
}
}
}
これを可能にしているのは各ビューのプロパティの宣言時にby bindView(ID)
と記述したおかげです。bindView
メソッドはKotter Knifeが提供するAPIです。プロパティ宣言のあとにby
と記述しているのはKotlinのDelegated Propertyという機能を使うためです。詳細は割愛しますが、Delegated Propertyとはプロパティへアクセスがあったときに、その処理を別のオブジェクトに委譲するしくみです。Kotter KnifeではこのDelegated Propertyを使って、プロパティとして保持しているビューに初めてアクセスがあったときに、ビューを取得するコードが発動するように作られています。
Anko
次に紹介するのはJetBrainsにより開発されているライブラリ、Anko [1] です。Android開発を簡単にする便利なAPIが数多くそろっていますが、目玉機能はUIレイアウトを構築するDSL(Domain Specific Language:ドメイン特化言語)です!
Android開発では通常、XMLでレイアウトを組んでJavaコードからそれを利用するという流れが一般的なのはみなさんご存じのとおりです。AnkoはレイアウトをXMLファイルとしてではなく、Kotlinコード上で組み上げるアプローチを提案しています。Kotlinで作成されたDSLを使うので、Kotlinの恩恵をそのまま受けられます。つまり簡潔、型安全、NULL安全ということです。
まずは非常に簡単な例をご覧入れましょう。リスト9 では、押すとトーストが表示されるボタンが1つ表示されるようなアクティビティを作っています。
リスト9 Ankoはこんな感じでUIを表現する
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
verticalLayout {
button("Click me!") {
onClick {
toast("Hello")
}
}
}
}
}
Kotlinコードですので、Kotlinでできることは何でもできます。たとえばレイアウトの使い回しはどうするのか、という問題はリスト10 のように関数に出すのも1つの方法です。
リスト10 UI構築部分を関数として切り出す
class MainActivity : Activity() {
override fun onCreate(savedInstanceState:
Bundle?) {
super.onCreate(savedInstanceState)
val button = createLayout()
button.setOnClickListener {
toast("Hello")
}
}
}
fun Activity.createLayout(): Button {
var button: Button? = null
verticalLayout {
// ボタンのテキストとしてリソースIDも指定できる
button = button(R.string.click_me) {
// ボタンのテキストサイズを指定
textSize = 24f
}.layoutParams {
// マージンを指定
margin = dip(20)
}
}
// ボタンを返す
return button ?: throw AssertionError()
}
拡張関数createLayout
で、Ankoを使ってレイアウトを構築しています。そしてAnkoを使って生成したボタンを返して、関数の呼び出し元でボタンにクリックリスナを登録しています。Ankoはまだ発展途上のライブラリで、筆者自身もベストプラクティスを模索中です。
肝心の導入方法ですが、簡単です。リスト11 の1行をgradle.buildのdependencies
に追記するだけです。とても面白いライブラリですのでぜひ使ってみてください。
リスト11 Anko導入
compile 'org.jetbrains.anko:anko:0.6.2-15'
Kotlin Extensions for Android
最後に紹介するのはKotlin Extensions for Androidというツールです。モチベーションとしてはKotter Knifeと同じくfindViewById
の排除です。しかしKotlin Extendions for Androidはさらに一歩進めて、プログラマがビューのマッピングを行ったり指定したりするような記述すら必要ありません。
簡単な具体例を示します。リスト12 はactivity_main.xml
という名前のレイアウトファイルです。そして、このレイアウトファイルを使うMainActivity
の定義がリスト13 です。
リスト12 activity_main.xml
<RelativeLayout xmlns:android="http://schemas.
android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/helloButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello"/>
</RelativeLayout>
リスト13 ビューオブジェクトが自動生成されている
package com.taroid.sample
import android.app.Activity
import android.os.Bundle
import android.widget.Toast
import kotlinx.android.synthetic.activity_main.helloButton
public class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
helloButton.setOnClickListener {
Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show()
}
}
}
リスト13では、onCreate
内でいきなり登場するhelloButton
に対してクリックリスナを登録しています。Kotter Knifeのときと異なり、helloButton
はプロパティに宣言されていなければDelegated Propertyも使用されていません。このhelloButton
、どこからやってきたのかと言うとKotlin Extensions for Androidによって生成され、それをインポートすることでMainActivity
内で使えるようにしています。自動生成されるビューの完全な名前を一般化すると「kotlinx.android.synthetic.<レイアウトファイル名>.< リソースID>
」のような形式になります。
Kotlin Extensions for Androidを導入するには、まずプラグインをインストールします。同名のプラグインをAndroid Studioにインストールし、再起動します。そしてbuild.gradleをリスト14 のように編集して、使えるようになります。
リスト14 Kotlin Extensions for Android導入
buildscript {
// (略)
dependencies {
// (略)
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
}
}
まとめ
Android Studioにプラグインを入れるだけで、すぐにKotlinによるAndroidプログラミングを体験できます。KotlinコードからシームレスにAndroidのAPIを呼び出せます。Kotlinの独特な文法、たとえば拡張関数やNULL安全などもAndroid上で動きます。
ボタンのクリックリスナのような、抽象メソッドが1つしかないインターフェースとして関数リテラルを使うことができます。クリックリスナの登録のときに、匿名クラスを書いて、メソッドをオーバーライドするようなノイズの多い記述をKotlinではしなくて済みます。
Context
を引数に取るような便利メソッドの定義の際には、拡張関数が非常に威力を発揮するでしょう。Context
に対する拡張関数とすれば、呼び出し側のコードが目に優しく直感的なスタイルになります。
便利なライブラリ・ツールを3つ紹介しました。Kotter KnifeはButter KnifeのKotlin版で、Delegated PropertyというKotlinの機能をうまく利用して実現されているView Injectionライブラリです。AnkoはAndroid開発におけるDSLセットで、とくにUIレイアウトが興味深いです。Kotlinの簡潔で安全な特長をUIレイアウトに活かせるのはうれしいです。Kotlin Extensions for AndroidはView Injectionのためのツールで、ビューのマッピングが全自動なため作業が減り、コードもすっきりします。
おわりに
全6回に及ぶKotlin入門連載、いかがでしたか? 業務でKotlinを使う日は遠からず来るのではないかと期待しています。現時点でKotlinはβ版という位置づけですが、そろそろバージョン1.0がリリースされそうな気配を感じています。今後も筆者のブログでKotlin情報の発信は続けていくので、新しいマイルストーンがリリースされたときなどにはチェックしてください。
Kotlinでみなさまのプログラミングが少しでも快適に、そして今よりもっと楽しくなればエバンジェリスト[2] 冥利に尽きます。Let's enjoy Kotlin!
第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識
第2特集
「知りたい」「 使いたい」「 発信したい」をかなえる
OSSソースコードリーディングのススメ
特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画]Red Hat Enterprise Linux 9最新ガイド
短期連載
今さら聞けないSSH
[前編]リモートログインとコマンドの実行
短期連載
MySQLで学ぶ文字コード
[最終回]文字コードのハマりどころTips集
短期連載
新生「Ansible」徹底解説
[4]Playbookの実行環境(基礎編)