我正在为我的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 }
...
}
答案 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
)作为第一个参数,然后使用第二个参数可以是您的字符串。