Realm4.3.0でのKotlinサポート追加


※ これはAndroid その2 Advent Calendarの10日目の記事です。


2017/12/05にRealm 4.3.0が発表されました!
このアップデートでRealm + Kotlinが強化されたので紹介です!

Overview

  • T::class.javaなんてもう書かないで良いよ!
  • RealmModelのメソッド呼び出しが美しく書けるよ!
  • Kotlinでinは予約語だよねoneOf使って良いよ!

T::class.javaなんてもう書かないで良いよ!

いままでは

Kotlinを使っている場合、Realmでクエリを記述は以下のようになります。(公式引用)

Sample.kt
// Instead of this…
val dog = realm.createObject(Dog::class.java)  // create a Dog
realm.delete(Dog::class.java) // Delete All Dogs
realm.where(Dog::class.java)
     .findAllAsync()
     .asFlowable() // Fetch all dogs asynchronously as an RxFlowable

これらの引数にはJavaのClassインスタンスを渡す必要があるためです。
ちょっと気持ち悪いですよね。

これからは

reified type paramterを用いた拡張関数の定義により

Sample.kt
// You do this
val dog = realm.createObject<Dog>()  // create a Dog
realm.delete<Dog>() // Delete All Dogs
realm.where<Dog>()
     .findAllAsync()
     .asFlowable() // Fetch all dogs asynchronously as an RxFlowable

のように記述できます!(公式引用)

これに尽きちゃうのですが、簡単に実装を見てみましょう。

Extensionの実装

恩恵をうけるのは以下のメソッドになります。
* where
* delete
* createObject

ここではwhereを見てみることにします。

RealmExtensions.kt
inline fun <reified T : RealmModel> Realm.where(): RealmQuery<T> {
    return this.where(T::class.java)
}

見て取れる通り、reifiedを使ってJavaのClassインスタンスを取得することで実現しているようです。
Reified Type Parametersは関数の中でTを実際の型として扱える機能ですね。便利だ…

RealmModelのメソッド呼び出しが美しく書けるよ!

補足

Realmでは、RealmObjectクラスを継承することでモデルを定義します。

ExampleModel.kt
open class Example(
        open var name: String = "",
        open var status: Int = 0,
) : RealmObject() {}

アプリによっては、別のベースモデルクラスを継承させたいという場面はよくあるとおもいますが、
この場合、RealmObjectを継承する以外の選択肢としてRealmModelインタフェースと@RealmClassアノテーションを用いる方法があります。

これを用いると

ExampleModel.kt
@RealmClass
open class Example(
        open var name: String = "",
        open var status: Int = 0,
) : RealmModel {}

のようになります。

いままでは

RealmObjectがインスタンスメソッドとして提供していたメソッドは、提供されているstaticメソッドを利用する必要がありました。

Sample.kt
// RealmObjectを継承する場合
example.isValid()
example.addChangeListener(listener)

// RealmModelを実装する場合
RealmObject.isValid(example)
RealmObject.addChangeListener(example, listener)

これからは

拡張メソッドとして提供され、RealmModelを実装させた場合でも

Example.kt
@RealmClass
open class Example(
        open var name: String = "",
) : RealmModel {}

example.isValid()
example.addChangeListener(listener)

のように記述することができます!美しい!!

こちらも簡単に実装を見てみましょう。

Extension実装

恩恵をうけるのは以下のメソッドになります。
* deleteFromRealm
* isValid
* isManaged
* isLoaded
* load
* E.addChangeListener
* E.removeChangeListener
* removeAllChangeListeners

ここではdeleteFromRealmを見てみることにします。

RealmModelExtensions.kt
fun RealmModel.deleteFromRealm() {
    RealmObject.deleteFromRealm(this)
}

簡単なExtensionですが、圧倒的に美しくなりますね。感謝。

Kotlinでinは予約語だよねoneOf使って良いよ!

いままでは

Kotlinではinが予約語のため、クエリで使用する場合以下のように書く必要があります。(公式引用)

Sample.kt
val dogs = realm
     .where<Dog>()
     .`in`("name", arrayOf("Larry", "Curly", "Moe"))

これからは

oneOfという拡張メソッドにより以下のように記述できます。(公式引用)

Sample.kt
val dogs = realm
     .where<Dog>()
     .oneOf("age", arrayOf("Larry", "Curly", "Moe"))

※ 注釈

Realmの公式ブログではanyOfを追加したよ!と記載されていますが、誤植?でしょうか。。
anyOfは実装されていないのでoneOfを使いましょう!

Extension実装

Stringの比較を行う場合の実装を見てみます。

RealmQueryExtensions.kt
fun <T : RealmModel> RealmQuery<T>.oneOf(propertyName: String,
                                         value: Array<String?>,
                                         casing: Case = Case.SENSITIVE): RealmQuery<T> {
    return this.`in`(propertyName, value, casing)
}

ちょっとしたことですが、Kotlinでの実装が気持ちよくなりますね!!

まとめ

著名なライブラリがKotlinで使いやすいようアップデートするケースを最近良く見ますね!
Kotlinはきもちよく書きたいですよね!素敵!!

Links