问题描述
我在firebase上的数据使用了很多字段类型的字段,但实际上是枚举值(我检查了我的验证规则)。要将数据下载到我的Android应用中,请按照指南。简而言之,您可以为执行转换的 String
属性实现一个委托,并实际获取/设置存储 enum
值的另一个属性的值,然后将 String
属性委托给它。
一个可能的实现如下所示:
$ p $ class $ EnumStringDelegate< ;>(
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 this(thisRef).toString())
}
operator fun setValue(thisRef:Any?,property:KProperty< *> ;, value:String){
enumValue = java.lang.Enum .valueOf(enumClass,stringToEnumName(value))
otherProperty.setter.call(thisRef,enumValue)
}
}
$ p $注意:此代码需要您添加Kotlin反射API,,作为项目的依赖项。使用Gradle,使用 compileorg.jetbrains.kotlin:kotlin-reflect:$ kotlin_version
。
这将在下面解释,但首先让我添加一个方便的方法来避免直接创建实例:
inline fun T :: class.java,
property,
String :: toLowerCase,
String :: toUpperCase )
以及您的课程的使用示例:
//如果在其他地方不需要`str`,则enum类可以缩写为:
enum class WeightUnit {KG,LB}
$ b $ class UserData:BaseModel(){
// ...为了清晰起见省略了更多的字段
@Exclude
var weightUnitEnum:WeightUnit = WeightUnit.KG
var weightUnit:String by enumStringLowerCase(UserData :: weightUnitEnum)
}
现在解释:当你通过enumStringLowerCase(UserData :: weightUnitEnum)编写 我添加的便利函数使您不必在属性声明中编写 基本上,这个解决方案将代表 使用这种技术,您可以在属性之间实现任何其他转换,例如您提到的 number到timestamp 。 My data on firebase uses many fields which have string type, but really are enum values (which I check in my validation rules). To download the data into my Android app, following the guide, the field must be a basic Now, while this works, it's not really clean: So, is there any way to do this properly? Some library maybe? Or some way to write a magic "field wrapper" that would automatically convert strings to enums, or numbers to timestamps, and so on, but is still compatible with Firebase library for getting/setting data? (Java solutions are welcome too :) ) If the conversion between a property with your To say it short, you can implement a delegate for One possible implementation would look like this: This will be explained below, but first let me add a convenience method to avoid creating the instances directly: And a usage example for your class: Now, the explanation: When you write The convenience function I added saves you from the necessity of writing Basically, this solution saves you from the boilerplate code that represents a property of Using this technique, you can implement any other conversion between properties, like the number to timestamp you mentioned. 这篇关于Firebase:在Kotlin / Java中使用枚举字段的干净方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持! var weightUnit:String时,委托
String
属性到构造的委托对象。这意味着当访问属性时,调用委托方法。然后,委托对象依次使用引擎盖下的 weightUnitEnum
属性。
UserData :: class.java
网站(使用),并提供转换函数为 EnumStringDelegate
(您可以随时创建具有不同转换的其他函数,甚至可以创建一个将转换函数接收为lambdas的函数)。 b
$ b enum
类型的样板代码从一个字符串
属性,给定转换逻辑,并且还允许您摆脱 enum
中的冗余代码,如果您不在其他地方使用它。
String
. I know I can work around this with a second (excluded) field which is an enum, and set this basing on the string value. A short example: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
itself is (1) kinda long for anenum, (2) the insides are repeated for every enum. And I have more of them.created
field above is really a timestamp,not a Long
.Map<SomeEnum, Timestamp>
...enum
value and another property of String
type is enough, this can be easily done in a flexible way using Kotlin delegated properties. String
properties which performs the conversion and actually gets/sets the value of another property storing the enum
values, and then delegate the String
property to it.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)
}
}
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)
, you delegate the String
property to the constructed delegate object. This means that when the property is accessed, the delegate methods are called instead. And the delegate object, in turn, works with the weightUnitEnum
property under the hood. UserData::class.java
at the property declaration site (using a reified type parameter) and provides the conversion functions to EnumStringDelegate
(you can create other functions with different conversions at any time, or even make a function that receives the conversion functions as lambdas).enum
type as a String
property, given the conversion logic, and also allows you to get rid of the redundant code in your enum
, if you don't use it anywhere else.