【问题标题】:Android Room type convert multiple enum typesAndroid Room 类型转换多种枚举类型
【发布时间】:2018-06-29 13:03:22
【问题描述】:

我正在为我的 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 }

    ...
}

【问题讨论】:

  • TypeA 和 TypeB 是 2 个不同的枚举类还是单个枚举的一部分?
  • 不同的枚举类
  • 我猜不可能在一个转换类中为 2 个枚举类创建多个类型转换器。由于您有 2 个TypeA 和TypeB,您应该创建另一个特定于TypeB 的转换器。或者,如果两者在结构上相似,您可以使用父 interface 并使 TypeA 和 TypeB 继承该接口并为该接口编写一个转换器,该转换器可以将对象转换为/从对象转换为 TypeA 和 @ 987654331@
  • 我明白了,你介意告诉我如何编写这样的界面吗?假设所有类型的形式为enum class TypeA(val abbrv: String)

标签: enums kotlin android-room


【解决方案1】:

正如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>()

【讨论】:

  • 其实你的不行。您的扩展程序可能有问题?
  • 你能具体说说它对你不起作用吗?
【解决方案2】:

您可以使用组合接口来实现此目的,因为您无法为多个对象类型编写单个转换器类。这有点骇人听闻,但可能会起作用:

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

【讨论】:

    猜你喜欢
    • 2010-12-21
    • 1970-01-01
    • 2023-02-21
    • 1970-01-01
    • 1970-01-01
    • 2010-10-11
    • 2013-04-28
    相关资源
    最近更新 更多