LoginSignup
10
11

More than 5 years have passed since last update.

Kotlinの拡張関数でRealmにタイプセーフAPIを追加するライブラリを作った

Last updated at Posted at 2015-11-15

できたもの

【2017/01/09追記】
約一年放置していましたが、本家での対応はまだみたいなので、2.2.2 に対応したバージョンを公開しました。

経緯

開発中のアプリにRealmを導入しようとして、サンプルに驚きました。
たとえばequalToで絞り込もうとするとこうなります。

Cat.kt
open class Cat : RealmObject() {
    @Required
    open var name: String = ""
}
Realm.getInstance(context).use {
    var cats = it.where(Cat::class.java)
        .equalTo("name", "たま") // (1)
        .equalTo("name", false) // (2)
        .findAll()
}

問題点はふたつ。

  1. name というフィールド名をベタ書きすることになる
  2. name の型をコンパイル時に判定しないので、"たま"のところに 1 とか false とかも書けてしまう(実行時例外)

これを解決したいと思った時に思い出したのが、S2JDBCのタイプセーフAPIです。
http://s2container.seasar.org/2.4/ja/s2jdbc_typesafe.html

S2JDBCの実装と合わせるならRealmQuery側に受け取り口が必要ですが、Kotlinならさくっとメソッドを追加できます。
どうせなら、RealmQueryにタイプセーフなメソッドを直接生やしてみよう。
ということで作ってみた。

ライブラリの内容

やってることは以下の通り。

(1) APTで @RealmClass が付与されているクラスのフィールド名クラスを自動生成
なお、@RealmClassは RealmObject に指定されているので、何もしなくて大丈夫です。

CatNames.java
// Generated class
public class CatNames {
  public static KRequiredStringPropertyName name() {
    return new KRequiredStringPropertyName("name");
  }
}

(2) Kotlinの拡張関数で、タイプセーフなメソッドを追加

KotliName.kt(抜粋)
fun <E : RealmObject> RealmQuery<E>.equalTo(fieldName: KRequiredPropertyName<String>, value: String) : RealmQuery<E> = equalTo(fieldName.name(), value)

// @Requiredなしの場合はこっち(valueがnull可)
fun <E : RealmObject> RealmQuery<E>.equalTo(fieldName: KNullablePropertyName<String>, value: String?) : RealmQuery<E> = equalTo(fieldName.name(), value)

こんな感じになりました。

Realm.getInstance(context).use {
    var cats = it.where(Cat::class.java)
        .equalTo(CatNames.name(), "たま") // nameって自分で書く必要が無い
        .equalTo(CatNames.name(), false) // String以外はコンパイルエラー
        .equalTo(CatNames.name(), null) // nameはnull不可なのでコンパイルエラー
        .findAll()
}

RealmQueryのメソッドは一通り実装してみたので、よかったらどうぞ。
https://github.com/75py/KotliNames

補足 公式サポートは?

タイプセーフAPIについては、RealmのIssueにも上がっているものの、「現時点では優先度が低い」ということらしいです。
https://github.com/realm/realm-java/issues/1744#issuecomment-155158923

KotliNamesはJavaでも使えるようにしてありますが、拡張関数なしではせいぜい タイポ対策にしかなりません。
ベタ書きよりはマシですが。

Javaでのタイプセーフに関しては、公式にサポートされるのを待ったほうが賢明かと思います。
(一応kotlinamesjにJava向けコードを置く余地は残しているので、気が向いたら書くかもしれませんが。)

補足 RealmObjectをKotlinで書く場合の注意点

RealmObjectをKotlinで書く場合はいろいろ注意が必要です。
Javaに変換されたときどうなるかを必要以上に意識することになりますので、おとなしくJavaで書いておいた方が無難かもしれません。

Cat.kt
// KotlinではデフォルトがJavaで言うfinalなので、クラス・フィールドにopen指定が必要
// 【2017/01/09追記】 1.0.6で All-open compiler plugin が公開されたので、今後はこれを使うのが良いでしょう
open class Cat : RealmObject() {
    @Required
    open var name: String = "" 
    // Javaだと public String name;
    // 特に問題なく動いているっぽい

    open var age: Int = 0 
    // Javaだと public int age;
    // プリミティブ型になるので、@RequiredなしでNULL不可になる

    open var weight: Double? = 0.0 
    // Javaだと public Double weight;
    // これはNULL可

    @Required
    open var hoge: Int = 0 
    // int型になるので、「プリミティブ型には@Requiredつけられないよ!」とRealm先生に怒られる
}

その他言いたいこと

  • Kotlinかわいいよー 【2017/01/09追記】 Dagger2との併用は茨の道(自力とググり力が試される)
10
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
11