In this post I want to share why I might not convert all my AutoValue classes to Kotlin data classes at this moment.
Don’t get me wrong. I love Kotlin, and I am slowly converting my existing project to Kotlin. However, I found in my particular use case that the data class might not be better than Auto Value class.
Before we jump into the issues directly, let me define the context of my use case first:
- The value classes represents JSON fetched from backend server. I don’t create the object myself. It is done via retrofit and gson so I don’t need extra
builder
declarations (which can be cumbersome to maintain too) - I need
Parcelable
implementation. - I cannot commit to 100% kotlin.
- I don’t want to write any extra code (type adapters, parcelable fields) for my value classes.
It is my specific use case, and it might not be the same as yours.
A sample value class in AutoValue:
|
|
With the help of AutoValue extensions like auto-value-gson and auto-value-parcel, I get a Person class with Parcelable
implemented and a gson TypeAdapter
generated along with nice implementations of hashCode()
, equals()
, and toString()
. It’s straightforward enough and I don’t really write code for my value classes.
The equivalent data class in Kotlin
|
|
Yes, it’s even shorter and I love it! But wait. It does not have Parcelable
and TypeAdapter
?
This is the problem I am running into. I have done some research but I have yet to find a good solution. There are some libraries or plugins that address the problem, but they are not a “1:1” replacement for AutoValue.
Parcelable for data class
- Parcelable Code Generator - It’s a IntelliJ plugin that generates necessary parcelable fields and methods. It works great, but the generated code lives in your codebase and becomes something you have to maintain. (the same argument that Hadi Hariri gives to IDE plugins for Java pojo)
- PaperParcel - A annotation processor available for data classes. It eliminates “some” boilerplate code when implementing
Parcelable
interface.
(Quote from PaperParcel docs)
|
|
Optional: If you don’t mind a minor amount of reflection, the paperparcel-kotlin module provides PaperParcelable. PaperParcelable is an interface with default implementations written for describeContents and writeToParcel(…) so you don’t have to write them yourself, e.g.:
|
|
To reduce most code, you need to use reflection and include kotlin-reflect
module. Reflection is not that terrible in my opinion, and I use multidex
already. However it is something you have to consider when switching from auto value classes.
JSON deserialization for data class
Many developers including me might think gson will just work with kotlin data class, but the result might be different:
|
|
When you run it, it compiles and runs, but you get:
|
|
Surprised? I was, but if you look at one [moshi issue], Jake Wharton commented:
Kotlin’s nullability is a facade and it’s very easy to put nulls in a backing field whose property was declared as non-nullable when using reflection, as this library does heavily.
This is actually a request for a @Json(required=true) annotation which will put presence check before returning a class instance to application code. Although does an explicit null in the JSON actually fulfill the requirement for presence? Or does required also imply non-null?
Gson’s default deserialization has the same issue. It uses java reflection to create instances and set the value directly on the backing fields.
So in order to parse JSON into Kotlin data class, you have two options:
- Write your own TypeAdapters, but this becomes a lot of code and write and maintain
- Use Moshi or Jackson with their
kotlin-module
, but again they both use kotlin reflection to access property information. It’s something you do not need when using AutoValue.
Conclusion
Stay with Auto Value (for now) if you are already using it, since with those well developed extension libraries Auto value might serve you better than data classes.
My wish list for data class:
- An Android platform specific code generation (by Google and Jetbrains) for parcelable fields since data class is a built-in language, not a library we can easily tweak or fork.
- An annotation processor that generates type adapters for data classes just like
auto-value-gson
. - Some “creative ways” from Square engineers (I believe they can as they always do!)