KotlinでAndroid開発するのに便利なライブラリ: KotterKnife

[KotterKnife](https://github.com/JakeWharton/kotterknife)は、Androidの[Kotlin](http://kotlinlang.org/)用**ViewInjection**ライブラリです。Java用の[ButterKnife](http://jakewharton.github.io/butterknife/)のKotlin版と言えます。開発者はButterKnifeと同じく我らが神[Jake Wharton](https://github.com/JakeWharton)さんです。

## 前提知識

### ViewInjectionライブラリ

Android開発で頻繁に登場する`findViewById`などをいい感じにやってくれるライブラリです。ButterKnifeの他に[AndroidAnnotations](http://androidannotations.org/)や[RoboGuice](https://github.com/roboguice/roboguice)などがあります。

### Kotlin

JetBrainsが中心に開発しているJVM言語です。Android開発にも対応しています。KotlinではButterKnifeやAndroidAnnotationsが使用できないのでViewInjectionライブラリの登場が待たれました。
KotlinでAndroidアプリ開発を始めるには[@sys1yagi](https://twitter.com/sys1yagi)さんの「[KotlinでAndroidアプリケーションを作ってみた2014初夏](https://speakerdeck.com/sys1yagi/kotlindeandroidapurikesiyonwozuo-tutemita2014chu-xia)」が非常にわかりやすいです。

## KotterKnife導入

導入は簡単です。下記のようにbuild.gradleを編集してください。

```
dependencies {
    // (略)
    compile 'com.jakewharton:kotterknife:0.1.0-SNAPSHOT'
}
repositories {
    // (略)
    maven {
        url 'https://oss.sonatype.org/content/repositories/snapshots/'
    }
}
```

## コード例

KotterKnifeの使用前(before.kt)と使用後(after.kt)のコード例を示します。

```before.kt
public class MainActivity : Activity() {

    var nameEditText: EditText? = null

    var submitButton: TextView? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        nameEditText = findViewById(R.id.name_edit_text) as EditText
        submitButton = findViewById(R.id.submit_button) as Button
        submitButton!!.setOnClickListener {
            val name = nameEditText?.getText().toString()
            Toast.makeText(this, name, Toast.LENGTH_SHORT).show()
        }
    }
}
```

before.ktでは通常通り`findViewById`でビューを取得し、`as`キーワードによりキャストしています。

```after.kt
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()
        }
    }
}
```

after.ktでは`findViewById`の代わりに`bindView`というKotterKnifeが提供する関数を使用しています。`bindView`が適切にビューを各プロパティ(`nameEditText`と`submitButton`)にバインドしてくれます。ビューがバインドされるタイミングは、そのプロパティへの最初のアクセスのときです。

## 解説

今回の例では、便宜的にプロパティとして`nameEditText`と`submitButton`を持っています。
Kotlinでは`var`または`val`キーワードを使用して変数(ここではプロパティ)を宣言できますが、`var`は書き換え可能な変数で`val`は読み取り専用の変数です。原則として`val`を使用することが望ましいのですが、プロパティの宣言時にはビューを取得できないため、before.ktではプロパティを`var`として宣言し、一時的に`null`をセットして後でビューに書き換えています。
一方after.ktでは`by bindView(resId)`のおかげでプロパティを`val`で宣言できます。`bindView`の使用者はキャストをする必要がなく、また型推論が働くために型指定の重複を避けられます。次のコードはすべて正しくコンパイル、実行されます。

```kotlin
// プロパティ

// 型の省略なし
val a: Button by bindView<Button>(R.id.submit_button)

// bindViewの型パラメータを省略
val b: Button by bindView(R.id.submit_button)

// 変数の型アノテーションを省略
val c by bindView<Button>(R.id.submit_button)
```

Kotlinでは`null`を代入できる変数と、代入できない変数を型として表現します(便宜的に`null`を代入できる型をNullable型、代入できない型をNotNull型と呼ぶことにします)。Kotlinでは基本的にデフォルトですべてNotNull型です。Nullable型は特別扱いされます。NPE(NullPointerException)を起こさせないための工夫です。そのためNullable型を扱うことは安全ですが、コーディングの上で煩わしく感じることもあります。
before.ktでは、各プロパティはNullable型です。プロパティ宣言時にはビューを取得できないので`null`をセットせざるを得ません[1]。after.ktの各プロパティはNotNull型です。その代わりに`bindView`はビューが見つからない場合などに例外を投げます[2]。ビューが見つからない場合などはたいていプログラマのミスなのでバインド時に例外で知らせてくれるのはいいアプローチだと思います。
KotlinのNullable型を特別扱いしてNPEを起こさせないための仕組み(Null-safety)については下記の資料を参照してください。
* [Kotlin Null-Safety機構について](https://speakerdeck.com/ntaro/kotlin-null-safetyji-gou-nituite)
* [KotlinのNullable型をモナドっぽくしてみた](http://taro.hatenablog.jp/entry/2014/04/06/144811)

## おまけ: タネ明かし

KotterKnifeが使用しているKotlinの言語機能は**Delegated Property**と呼ばれるものです。これはプロパティへのアクセスを他のオブジェクトに委譲する仕組みです。

```kotlin
class MyClass {
    var myProperty1: String = "普通のプロパティ"
    var myProperty2: String by 委譲先のオブジェクト
}
```

「委譲先のオブジェクト」は、例えば次のようなクラスのインスタンスなどです。

```kotlin
class Decorator {
    private var value: String? = null

    // プロパティの値の参照が、このメソッドに委譲されます
    fun get(thisRef: Any, prop: PropertyMetadata): String {
        return "${prop.name}の値は${value}だよ♪"
    }

    // プロパティの値の変更が、このメソッドに委譲されます
    fun set(thisRef: Any, prop: PropertyMetadata, value: String) {
        this.value = "☆☆☆${value}☆☆☆"
    }
}
```

```kotlin
class Person {
	var name: String by Decorator()
}

fun main(args: Array<String>) {
	val taro = Person()
	taro.name = "Taro"
	println(taro.name) // => nameの値は☆☆☆Taro☆☆☆だよ♪
}
```

詳細はKotlin公式サイトか、「[KotlinでAndroidアプリ開発: Viewのインジェクションを実現する](http://taro.hatenablog.jp/entry/2014/01/22/204806)」を参照してください。

## まとめ

KotterKnifeはViewInjectionライブラリで、KotlinでのAndroidアプリ開発におけるボイラープレートを減らしてくれます。それを実現しているのはKotlinのDelegeted Propertyという機能です。
現時点でKotterKnifeのプロダクトコードはたったの99行なので、興味のある方は読んでみてください。

--

[1] 後述のDelegated Propertyを使用すると、宣言時に値が確定していないプロパティをNotNull型にできます。
[2] 例外を投げずに`null`を返す`bindOptionalView`などのメソッドもあります。