【问题标题】:How to get the value of an enum using generics?如何使用泛型获取枚举的值?
【发布时间】:2018-11-30 19:08:10
【问题描述】:

我想为这个问题提供上下文。我已经看到了独特的解决方案,人们创建将从 SharedPreferences 而不是支持字段读取/写入的委托。对字符串执行此操作,例如:

class SharedPrefsString(private val sharedPrefs: SharedPreferences) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return sharedPrefs.getString(property.name, "")
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        sharedPrefs.edit().putString(property.name, value).apply()
    }
}

var myString: String by SharedPrefsString(myPrefs)

但是,我希望我可以对通用枚举做同样的事情,因为所有枚举都有一个 valueOf(string) 方法,但下面的方法不起作用。我在 cmets 中放了一些我尝试过的东西:

class SharedPrefsEnum<T : Enum<T>>(private val sharedPrefs: SharedPreferences) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        // Doesn't work, can't reference T, which makes sense. 
        // return T.valueOf(sharedPrefs.getString(property.name, ""))

        // Can't use reified type here, which makes sense.
        // return enumValueOf<T>(sharedPrefs.getString(property.name, ""))
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        sharedPrefs.edit().putString(property.name, value.name).apply()
    }
}

var myEnum: MyEnum by SharedPrefsEnum(myPrefs)

这样的事情可能吗?

【问题讨论】:

  • 您可能需要获取ClassKClass。但是,不支持具体的类级泛型,因此您必须手动完成。不过可能有更好的方法
  • @Zoe 一旦我有对类的引用,我能做什么?弄清楚如何用反射调用“valueOf”?
  • IIRC, valueOf 是静态的,所以你可以尝试使用反射来获取方法,然后调用它。使用ClassKClass 应该很简单,但您可能会得到SecurityException。我没有太多地接触反射,所以我不熟悉如何正确避免这些
  • @Zoe 感谢您为我指明正确的方向。 :)

标签: generics kotlin


【解决方案1】:

根据您的答案,您可以避免使用 enumConstants 属性的反射:

class SharedPrefsEnum<T : Enum<T>>(
    private val sharedPrefs: SharedPreferences, 
    private val clazz: Class<T>
) : ReadWriteProperty<Any, T> {
    operator fun getValue(thisRef: Any, property: KProperty<*>): T {
        val enumName = sharedPrefs.getString(property.name, "")
        return clazz.enumConstants.find { it.name == enumName }!!
    }

    operator fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
        sharedPrefs.edit().putString(property.name, value.name).apply()
    }
}

然后您可以使用以下方法轻松添加委托:

inline fun <reified T : Enum<T>> sharedPreferences(prefs: SharedPreferences) = 
    SharedPrefsEnum(prefs, T::class.java)

允许您将其分配给:

private var myEnum: MyEnum by sharedPreferences(preferences)

但是,您可能希望将属性类型设置为可为空,就好像您的 SharedPreferences 不包含该值,这会在访问该值时引发异常。

【讨论】:

  • 哇!谢谢!很快就会尝试一下。
  • 做了一个小调整以在getValue 中提取一个val,以避免多次获取它
【解决方案2】:

我能够使用反射解决这个问题。感谢 Zoe 为我指明了正确的方向。

我也必须传入枚举类,但使用它我可以使用反射找到valueOf 并使用我从首选项中获得的字符串调用它。

class SharedPrefsEnum<T : Enum<T>>(private val sharedPrefs: SharedPreferences, private val clazz: Class<T>) {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
        val prefsString = sharedPrefs.getString(property.name, "")
        val method = clazz.getDeclaredMethod("valueOf", String::class.java)
        return method.invoke(null, prefsString) as T
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        sharedPrefs.edit().putString(property.name, value.name).apply()
    }
}

【讨论】:

    【解决方案3】:

    我正在使用这个:

    var lastUpdate by PersistedProperty("lastUpdate",0L)
    

    这是实现:

    class PersistedProperty<T>(var prefName:String, var default: T)
    {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return value!!
        }
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T){
            this.value=value
        }
    
        fun getFresh():T
        {
            value = getPreference(prefName, default)
            return value!!
        }
    
        var value:T? = null
            get(){
                if(field==null)
                    field=getPreference(prefName, default)
                return field
            }
            set(value){
                if(value==field)
                    return
                savePreference(prefName, value)
                field=value
            }
    
    }
    
    fun<T> savePreference(name:String, value:T,prefFile: String? = null)
    {
        val sharedPreferences = App.instance.getSharedPreferences(prefFile?:App.instance.packageName+ "_preferences",Context.MODE_PRIVATE)
        when(value)
        {
            is String->sharedPreferences.edit().putString(name, value).apply()
            is Int->sharedPreferences.edit().putInt(name, value).apply()
            is Long->sharedPreferences.edit().putLong(name, value).apply()
            is Float->sharedPreferences.edit().putFloat(name, value).apply()
            is Boolean->sharedPreferences.edit().putBoolean(name, value).apply()
            else -> throw(Exception("Not suported type"))
        }
    }
    

    【讨论】:

    • 这非常相似,但不幸的是我认为这对枚举没有帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多