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

时间:2017-01-14 15:13:44

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

我在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)有点长 enum,(2)每个枚举都重复内部。而且我有更多。
  • 它不仅是枚举,上面的created字段实际上是一个时间戳, 不是Long
  • 每个模型都会使用这些枚举字段很多次,这会使模型类充满可重复的代码......
  • 对于类型为Map<SomeEnum, Timestamp> ...
  • 的字段,帮助字段/函数变得更糟/更长

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

(欢迎Java解决方案:))

2 个答案:

答案 0 :(得分:5)

如果具有enum值的属性与String类型的其他属性之间的转换已足够,则可以使用Kotlin delegated properties以灵活的方式轻松完成此操作。

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

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

String

注意:此代码要求您将Kotlin反射API kotlin-reflect添加为项目的依赖项。使用Gradle,使用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) } }

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

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属性一起使用。

我添加的便利功能使您无需在属性声明网站(使用reified type parameter)编写weightUnitEnum,并将转换功能提供给UserData::class.java(您可以创建其他功能)任何时候都可以使用不同的转换函数,甚至可以创建一个接收转换函数的函数作为lambdas。)

基本上,在给定转换逻辑的情况下,此解决方案可以避免使用代表EnumStringDelegate类型属性作为enum属性的样板代码,并且还允许您删除冗余代码你的String,如果你不在其他任何地方使用它。

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

答案 1 :(得分:3)

我处于类似的情况和从而找到了你的问题,还有很多其他类似的问题/答案。

不能直接回答你的问题,但这就是我最终做的事情:我决定更改我的应用程序&amp;完全没有使用枚举数据类型 - 主要是因为Google开发门户网站的建议显示了枚举对应用程序性能的影响。请参阅以下视频https://www.youtube.com/watch?v=Hzs6OBcvNQE