Firebase:在Kotlin/Java中使用枚举字段的简洁方法?

que*_*zak 8 java android kotlin firebase firebase-realtime-database

我在firebase上的数据使用了许多具有字符串类型的字段,但实际上是枚举值(我在验证规则中检查).要按照指南将数据下载到我的Android应用程序中,该字段必须是基本字段String.我知道我可以使用枚举的第二个(排除的)字段解决这个问题,并根据字符串值设置此字段.一个简短的例子:

class UserData : BaseModel() {
    val email: String? = null
    val id: String = ""
    val created: Long = 0
    // ... more fields omitted for clarity
    @Exclude
    var weightUnitEnum: WeightUnit = WeightUnit.KG
    var weightUnit: String
        get() = weightUnitEnum.toString()
        set(value) { weightUnitEnum = WeightUnit.fromString(value) }
}

enum class WeightUnit(val str: String) {
    KG("kg"), LB("lb");
    override fun toString(): String = str
    companion object {
        @JvmStatic
        fun fromString(s: String): WeightUnit = WeightUnit.valueOf(s.toUpperCase())
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,虽然这有效,但它并不是很干净:

  • enum class本身是(1)有点长的枚举,(2)内部重复每个枚举.而且我有更多.
  • 它不仅仅是枚举,created上面的字段实际上是一个时间戳,而不是一个Long.
  • 每个模型都使用这些枚举字段很多次,这使得模型类具有可重复的代码......
  • 对于具有类型等类型的字段,辅助字段/函数变得更糟/更长Map<SomeEnum, Timestamp>...

那么,有没有办法正确地做到这一点?有些图书馆可能?或者某种方式来编写一个神奇的"字段包装器",它会自动将字符串转换为枚举,或者数字转换为时间戳,等等,但仍然与Firebase库兼容以获取/设置数据?

(也欢迎Java解决方案:))

hot*_*key 5

如果具有您的enum值的属性与另一个String类型属性之间的转换足够,则可以使用Kotlin委派属性以灵活的方式轻松完成此操作.

简而言之,您可以为String执行转换的属性实现委托,并实际获取/设置存储值的另一个属性的enum值,然后将String属性委托给它.

一种可能的实现方式如下:

class EnumStringDelegate<T : Enum<T>>(
        private val enumClass: Class<T>,
        private val otherProperty: KMutableProperty<T>,
        private val enumNameToString: (String) -> String,
        private val stringToEnumName: (String) -> String) {

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return enumNameToString(otherProperty.call(thisRef).toString())
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        val enumValue = java.lang.Enum.valueOf(enumClass, stringToEnumName(value))
        otherProperty.setter.call(thisRef, enumValue)
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:此代码要求您将Kotlin反射API添加kotlin-reflect为项目的依赖项.使用Gradle,使用compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version".

这将在下面解释,但首先让我添加一个方便的方法,以避免直接创建实例:

inline fun <reified T : Enum<T>> enumStringLowerCase(
    property: KMutableProperty<T>) = EnumStringDelegate(
    T::class.java,
    property,
    String::toLowerCase,
    String::toUpperCase)
Run Code Online (Sandbox Code Playgroud)

以及您班级的使用示例:

// if you don't need the `str` anywhere else, the enum class can be shortened to this:
enum class WeightUnit { KG, LB } 

class UserData : BaseModel() {
    // ... more fields omitted for clarity
    @Exclude
    var weightUnitEnum: WeightUnit = WeightUnit.KG
    var weightUnit: String by enumStringLowerCase(UserData::weightUnitEnum)
}
Run Code Online (Sandbox Code Playgroud)

现在,解释:

编写时var weightUnit: String by enumStringLowerCase(UserData::weightUnitEnum),将String属性委托给构造的委托对象.这意味着在访问属性时,将调用委托方法.然后,委托对象与weightUnitEnum引擎盖下的属性一起工作.

我添加的便利功能使您免于UserData::class.java在属性声明站点上写入的必要性(使用reified类型参数)并提供转换函数EnumStringDelegate(您可以随时创建具有不同转换的其他函数,甚至可以创建一个接收的函数)转换函数为lambdas).

基本上,这个解决方案可以节省您的样板代码,该代码表示enum类型属性作为String属性,给定转换逻辑,并且enum如果您不在其他任何地方使用它,也可以让您摆脱您的冗余代码.

使用此技术,您可以实现属性之间的任何其他转换,例如您提到的时间戳编号.


小智 5

我处于类似的情况,因此找到了您的问题,以及许多其他类似的问题/答案。

无法直接回答您的问题,但这就是我最终做的:我决定更改我的应用程序并且根本不使用枚举数据类型 - 主要是因为 Google 开发门户的建议显示了枚举对应用程序性能的影响。请参阅下面的视频https://www.youtube.com/watch?v=Hzs6OBcvNQE