I was wondering if anyone found a way to use kotlins extension functions as TypeConverter methods when using Room database. The code looks super clean, but I've not been able to find a way to add the converter to RoomDatabases array of converters:
enum class Status(val code: Int) {
ACTIVE(0), INACTIVE(1), COMPLETED(2)
}
@TypeConverter
fun Status.toInt() : Int = this.code // This could be shorter I know
@TypeConverter
fun Int.toStatus() : Status = when(this) { // This could also be shorter
Status.ACTIVE.code -> Status.ACTIVE
Status.INACTIVE.code -> Status.INACTIVE
Status.COMPLETED.code -> Status.COMPLETED
else -> {
Timber.e("Could not recognize Status")
throw IllegalArgumentException("Could not recognize Status")
}
}
Even the tests look super elegant:
class SampleConverterTest {
@Test
fun fromIntToActiveStatus() {
assertThat(Status.ACTIVE.toInt(), `is`(Status.ACTIVE.code))
}
@Test
fun fromIntToInactiveStatus() {
assertThat(Status.INACTIVE.toInt(), `is`(Status.INACTIVE.code))
}
@Test
fun fromIntToCompletedStatus() {
assertThat(Status.COMPLETED.toInt(), `is`(Status.COMPLETED.code))
}
@Test
fun fromActiveStatusToInt() {
assertThat(Status.ACTIVE.fromInt(), `is`(Status.ACTIVE))
}
@Test
fun fromInactiveStatusToInt() {
assertThat(Status.INACTIVE.fromInt(), `is`(Status.INACTIVE))
}
@Test
fun fromCompletedStatusToInt() {
assertThat(Status.COMPLETED.fromInt(), `is`(Status.COMPLETED))
}
}
I'm not super familiar with java kotlin interop, but is there any way to have these extensions as your TypeConverters?
EDIT:
Beware as @Luksprog pointed out in his answer, the Status.fromInt() extension method isn't a correct converter method. It would instead have to be an extension on Int. I've edited my question to show this.
First of all, one of the extension methods(fromInt()) isn't a real converter method. For that method Room expects a method in the form of(in java):
static Status fromInt(int code){...}
however the Kotlin compiler will generate(in java) something like
static Status fromInt(Status obj){...}
Room will not know what to do with this method which it needs to convert from the int in the database to your Status field.
Secondly, in their current form, those two extension methods are pretty useless. One is an alias for Status.ACTIVE.code and the other will simply return the instance on which is invoked.
You can't use those methods for the TypeConverters interface because that interface expects a class reference which groups the required static methods. You can't access in kotlin the java class(containing the methods) which will be generated by the kotlin compiler so you can't use it on the database.
If you want to keep your current extension methods but also create the required type converters methods you could use some indirection like below:
fun Status.toInt(): Int = StatusConverters.toInt(this)
fun Status.fromInt(): Status = StatusConverters.fromInt(this.code)
object StatusConverters {
@TypeConverter
@JvmStatic
fun toInt(status: Status): Int = status.code // This could be shorter I know
@TypeConverter
@JvmStatic
fun fromInt(code: Int): Status = when (code) { // This could also be shorter
Status.ACTIVE.code -> Status.ACTIVE
Status.INACTIVE.code -> Status.INACTIVE
Status.COMPLETED.code -> Status.COMPLETED
else -> {
Timber.e("Could not recognize Status")
throw IllegalArgumentException("Could not recognize Status")
}
}
}