Android 房间类型转换多个枚举类型

Android Room type convert multiple enum types

我正在为我的 Room 数据库编写一个类型转换器。我有几个自定义枚举 classes,当存储在数据库中时,我想将它们全部转换为其序数。因此,不是为每个 class 编写以下内容,是否有任何方法可以简化它(例如传递通用枚举类型)?

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 }

    ...
}

正如所讨论的 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>()

您可以使用组合接口来实现此目的,因为您无法为多种对象类型编写单个转换器 class。这有点老套,但可能会奏效:

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 作为第一个参数,然后第二个参数可以是你的字符串。