我在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解决方案:))
答案 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