Firebase:在 Kotlin/Java 中使用枚举字段的干净方式?
Posted
技术标签:
【中文标题】Firebase:在 Kotlin/Java 中使用枚举字段的干净方式?【英文标题】:Firebase: clean way for using enum fields in 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 解决方案 :))
【问题讨论】:
【参考方案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 库在从/向数据库获取/保存对象时跳过了委托属性。你可能知道如何解决这个问题吗?详情:***.com/questions/42737499/… @quezak 尝试使用@PropertyName
:***.com/questions/38681260/… 我没有任何使用 Firebase 的经验,但它似乎只适用于默认情况下的字段。【参考方案2】:
我处于类似情况,因此找到了您的问题,以及许多其他类似的问题/答案。
无法直接回答您的问题,但这就是我最终要做的事情:我决定更改我的应用程序并且根本不使用枚举数据类型 - 主要是因为 Google 开发门户网站的建议显示了应用程序上的枚举有多糟糕表现。见下方视频https://www.youtube.com/watch?v=Hzs6OBcvNQE
【讨论】:
以上是关于Firebase:在 Kotlin/Java 中使用枚举字段的干净方式?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Cloud Functions for Firebase 中使 HTTP 请求异步/等待?
Firebase云Firebase。如何在本地模式下在项目间移动数据
ReferenceError:进程未在 firebase-messaging-sw.js 中定义