Posted at

KotlinでParcelerを使う話

More than 3 years have passed since last update.

クラスでParcelableを実装するのは、めんどくさいので、JavaではParcelerを使っているのですが、それをKotlinで使おうという話です。


試してみる

まず、


Foo.kt

@Parcel

data class Foo(
val Hoge: Int,
val Fuga: String,
val Piyo: List<String>
)

こんなクラスを考えてみます。


build.gradle


・・・
dependencies {
・・・
compile "org.parceler:parceler-api:1.1.1"
kapt "org.parceler:parceler:1.1.1"
}


こんな風に準備して、いざ、Build。

エラー: Parceler: No @ParcelConstructor annotated constructor and no default empty bean constructor found.

こんな感じのエラーがでます。引数無しのコンストラクタがないですからね。


引数無しのコンストラクタ


Foo.kt

@Parcel

data class Foo(
val Hoge: Int,
val Fuga: String,
val Piyo: List<String>
) {
private constructor() : this(0, "", ArrayList())
}

としてあげることで、Parcelerを使用できます。

このコンストラクタはprivateにしておいても、Parcelerが生成するクラスではリフレクションで使用できるみたいなので、Parcelerのためだけな場合は、privateにしておくとよいでしょう。

また、問題なければ、


Foo.kt

@Parcel

data class Foo(
val Hoge: Int = 0,
val Fuga: String = "",
val Piyo: List<String> = ArrayList()
)

のように、デフォルト値を指定しておけば、引数無しのコンストラクタも作られるので、それでもかまいません。

ただ、この場合、不要なコンストラクタもできてしまうので、この方法は慎重に使う必要があります。


明示的なコンストラクタの指定


Foo.kt

@Parcel

data class Foo @ParcelConstructor constructor(
val Hoge: Int,
val Fuga: String,
val Piyo: List<String>
)

というように、使用するコンストラクタを指定してあげればできそうな気もするのですが、

エラー: Parceler: No corresponding property found for constructor parameter arg0

エラー: Parceler: No corresponding property found for constructor parameter arg1
エラー: Parceler: No corresponding property found for constructor parameter arg2

というエラーになってしまうので、素直に引数無しのコンストラクタを作ってあげた方が良いようです。


補足

Kotlinのインクリメンタル・コンパイラのせいなのか、kaptがまだまだなのか、はたまた、インスタントランの影響なのかはわかりませんが、ソースを修正しても、build/generated/source/kapt/ に生成されるソースが、そのままになっていることがあります(これで、かなり混乱した)。

意図したようにBuildできないときや、適当なタイミングで、 kaptディレクトリをサクッと削除 して試した方が良いみたいです。


その他の方法

Parcelable code generation( for kotlin's data class) というプラグインもあるので、Parcelerを使わずに、それでParcelableインターフェースのメソッドを生成するという方法もあります。

ただ、


Foo.kt

data class Foo(

val Hoge: Int,
val Fuga: String,
val Piyo: List<String>
) : Parcelable {

constructor(source: Parcel): this(source.readInt(), source.readString(), source.createStringArrayList())

override fun describeContents(): Int {
return 0
}

override fun writeToParcel(dest: Parcel?, flags: Int) {
dest?.writeInt(Hoge)
dest?.writeString(Fuga)
dest?.writeStringList(Piyo)
}

companion object {
@JvmField final val CREATOR: Parcelable.Creator<Foo> = object : Parcelable.Creator<Foo> {
override fun createFromParcel(source: Parcel): Foo {
return Foo(source)
}

override fun newArray(size: Int): Array<Foo?> {
return arrayOfNulls(size)
}
}
}
}


こんな感じになって、すっきりしたdata classの定義が台無しになってしまうので、あまり使いたくないなーと思います。