Kotlinのプロパティをタイプセーフに取得する

おはようございます。Kotlinアドベントカレンダー2015の22日目の記事でございます。 DBアクセスライブラリを作っていたときに見つけたものを紹介します。

Kotlinのプロパティをタイプセーフに取得する

Kotlinのプロパティは、Java8のメソッド参照のように書くことでプロパティ定義(kotlin.reflect.KProperty1<T, R>)を取得することができます。

// データクラスを定義
data class Person(val name: String, val age: String)

fun main(args: Array<String>): Unit {
    val prop1 = Person::name
    println(prop1.name) // => "name"

    val prop2 = Person::age
    println(prop2.name) // => "age"
}

コンパイル結果を確認していないので不確かですが、上記のプロパティ名を取得する処理については、コンパイル時に解決しているのかループで回してもあまりコストがかかっていませんでした。

もちろん、プロパティ名を変更した場合は参照箇所がコンパイルエラーになるため、プロパティ名をハードコーディングしていた箇所を置き換えることができます。

データベースの問い合わせ結果をマッピングする

Scala の Skinny Framework や Scalikejdbc の作者の @seratch さんが、Kotlin の DB アクセスライブラリを書いていました。

seratch/kotliquery https://github.com/seratch/kotliquery

ことりクエリーかわいい。自分のDBアクセスライブラリは置いといてこっち触りましょうw ことりクエリーでは、問い合わせ結果が Row という ResultSet をラップしたクラスで返ってきます。このクラスに上で書いたプロパティ参照を受け取るメソッドを追加することで、プロパティ名をハードコーディングしなくてもよくなります。

// 追加するメソッドの例
fun <T> get(prop: KProperty1<T, String?>): String? {
    val columnLabel = camelToSnake(prop.name) // camelCase を snake_case に変換するメソッド
    return string(columnLabel)
}

fun <T> get(prop: KProperty1<T, Int?>): Int? {
    val columnLabel = camelToSnake(prop.name)
    return int(columnLabel)
}

// データ型の数だけ続く・・・
// マッピング
val toMemberReflection: (Row) -> Member = { row ->
    // プロパティ名のハードコーディングがなくなる!
    Member(row.get(Member::id)!!, row.get(Member::name), row.get(Member::createdAt)!!)
}

この拡張をしてみたソースを置いておきます。

kotliquery/Row.kt at mapping-reflection · rabitarochan/kotliquery https://github.com/rabitarochan/kotliquery/blob/mapping-reflection/src/main/kotlin/kotliquery/Row.kt

まとめ

例として、データベースの結果を取得する方法をあげましたが、Webリクエストのパラメータを取得する、JSONシリアライズ/デシリアライズなど使える箇所はいろいろあると思います。 プロパティ以外にも、メソッド参照も取得できますし、JavaのClassも取得することができます。みなさんもKotlinのリフレクションを触ってみてください!

明日は mattak さんです!!