【问题标题】:Firebase: clean way for using enum fields in Kotlin/Java?Firebase:在 Kotlin/Java 中使用枚举字段的干净方式?
【发布时间】:2017-05-29 19:44:54
【问题描述】:

我在 firebase 上的数据使用了许多具有字符串类型的字段,但实际上是枚举值(我检查了我的验证规则)。要将数据下载到我的 Android 应用程序following the guide,该字段必须是基本的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())
    }
}

现在,虽然这可行,但它并不是很干净:

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

那么,有什么方法可以正确地做到这一点吗?也许是某个图书馆?或者以某种方式编写一个神奇的“字段包装器”,它可以自动将字符串转换为枚举,或将数字转换为时间戳,等等,但仍与用于获取/设置数据的 Firebase 库兼容?

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

【问题讨论】:

    标签: java android firebase firebase-realtime-database kotlin


    【解决方案1】:

    如果您的enum 值的属性与String 类型的另一个属性之间的转换就足够了,则可以使用Kotlin delegated properties 以灵活的方式轻松完成。

    简而言之,您可以为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)
        }
    }
    

    注意:此代码要求您将 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)
    

    还有一个你的类的用法示例:

    // 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)
    }
    

    现在,解释:

    当您编写var weightUnit: String by enumStringLowerCase(UserData::weightUnitEnum) 时,您将String 属性委托给构造的委托对象。这意味着当访问属性时,会调用委托方法。反过来,委托对象在后台使用 weightUnitEnum 属性。

    我添加的便利函数使您不必在属性声明站点编写UserData::class.java(使用reified type parameter)并提供转换函数到EnumStringDelegate(您可以随时创建具有不同转换的其他函数时间,或者甚至制作一个接收转换函数作为 lambdas 的函数)。

    基本上,这个解决方案将您从将enum 类型的属性表示为String 属性的样板代码中保存下来,给定转换逻辑,并且还允许您摆脱enum 中的冗余代码,如果你不在其他地方使用它。

    使用这种技术,您可以实现属性之间的任何其他转换,例如您提到的数字到时间戳

    【讨论】:

    • 感谢您的精彩解释 :) 我已经根据您的建议实现了一些东西,但最后似乎 Firebase 库在从/向数据库获取/保存对象时跳过了委托属性。你可能知道如何解决这个问题吗?详情:stackoverflow.com/questions/42737499/…
    • @quezak 尝试使用@PropertyNamestackoverflow.com/questions/38681260/… 我没有任何使用 Firebase 的经验,但它似乎只适用于默认情况下的字段。
    【解决方案2】:

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

    无法直接回答您的问题,但这就是我最终要做的事情:我决定更改我的应用程序并且根本不使用枚举数据类型 - 主要是因为 Google 开发门户网站的建议显示了应用程序上的枚举有多糟糕表现。见下方视频https://www.youtube.com/watch?v=Hzs6OBcvNQE

    【讨论】:

      猜你喜欢
      • 2021-06-04
      • 1970-01-01
      • 2010-09-18
      • 1970-01-01
      • 2021-12-22
      • 2022-01-18
      • 2019-06-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多