Android Room类型转换多种枚举类型

时间:2018-06-29 13:03:22

标签: enums kotlin android-room

我正在为我的Room数据库编写一个类型转换器。我有几个自定义的枚举类,当我将它们存储在数据库中时,我想将它们全部转换为普通的枚举类。因此,除了为每个类编写以下代码之外,还有什么方法可以简化它(例如,传递通用枚举类型)?

class Converter {

    @TypeConverter
    fun toOrdinal(type: TypeA): Int = type.ordinal

    @TypeConverter
    fun toTypeA(ordinal: Int): TypeA = TypeA.values().first { it.ordinal == ordinal }

    @TypeConverter
    fun toOrdinal(type: TypeB): Int = type.ordinal

    @TypeConverter
    fun toTypeB(ordinal: Int): TypeB = TypeB.values().first { it.ordinal == ordinal }

    ...
}

2 个答案:

答案 0 :(得分:10)

here所述,Room目前无法处理通用转换器。我认为您能做的最好的事情就是创建扩展程序,以更快地编写这些枚举转换器:

@Suppress("NOTHING_TO_INLINE")
private inline fun <T : Enum<T>> T.toInt(): Int = this.ordinal

private inline fun <reified T : Enum<T>> Int.toEnum(): T = enumValues<T>()[this]

这会将每对转换器简化为以下代码:

@TypeConverter fun myEnumToTnt(value: MyEnum) = value.toInt()
@TypeConverter fun intToMyEnum(value: Int) = value.toEnum<MyEnum>()

或者,如果您可能存储空值:

@TypeConverter fun myEnumToTnt(value: MyEnum?) = value?.toInt()
@TypeConverter fun intToMyEnum(value: Int?) = value?.toEnum<MyEnum>()

答案 1 :(得分:1)

您可以使用合成接口来实现此目的,因为您不能为多种对象类型编写单个转换器类。这有点hacky,但可能会起作用:

interface BaseType {
    val arg0: String

    fun asString() : String? {
        return when(this) {
            is TypeA -> "${TypeA::class.simpleName}$separatorParam$arg0"
            is TypeB -> "${TypeB::class.simpleName}$separatorParam$arg0"
            else -> null
        }
    }

    companion object {
        const val separatorParam = "::"
    }
}

enum class TypeA (override val arg0: String) : BaseType {
    A_ONE("argument 1"),
    A_TWO("argument 2");

    companion object {
        fun getValueTypes(arg0: String) : TypeA? = values().firstOrNull { it.arg0 == arg0 }
    }
}

enum class TypeB (override val arg0: String) : BaseType {
    A_ONE("argument 1"),
    A_TWO("argument 2");

    companion object {
        fun getValueTypes(arg0: String) : TypeB? = values().firstOrNull { it.arg0 == arg0 }
    }
}

class Converter {
    @TypeConverter
    fun fromBaseType(type: BaseType) : String? = type.asString()

    @TypeConverter
    fun toBaseType(param: String?) : BaseType? = param?.asBaseType()

    private fun String.asBaseType() : BaseType? {
        val stringArray = this.split(BaseType.separatorParam)
        val className : String? = stringArray[0]
        return when(className) {
            TypeA::class.simpleName -> TypeA.getValueTypes(stringArray[1])
            TypeB::class.simpleName -> TypeB.getValueTypes(stringArray[1])
            else -> null
        }
    }
}

然后,您需要在data class中使用一个函数来为您提供实际的TypeA或TypeB

data class MyDbModel(val baseType: BaseType) {
    inline fun <reified T: BaseType> getTypeAs() : T? = 
            when(baseType) {
                is TypeA -> TypeA.getValueTypes(baseType.arg0) as? T
                is TypeB -> TypeB.getValueTypes(baseType.arg0) as? T
                else -> null
            }
}

fun foo() {
    val model = MyDbModel(TypeA.A_ONE)
    val type = model.getTypeAs<TypeA>()
}

此方法的缺点是它仅适用于特定枚举中的唯一arg0,因为您可以使用序数,也可以使用生成的ID(例如R.id.a_one)作为第一个参数,然后使用第二个参数可以是您的字符串。