【问题标题】:Does Kotlin custom get execute method callKotlin 自定义获取执行方法调用吗
【发布时间】:2016-04-01 22:25:12
【问题描述】:

为了增加调用 SharedPreferences.Editor 的可读性,我想使用一个 Kotlin 变量,每次我需要一个新的 SharedPreferences.Editor 时都会执行“getSharedPreferences.edit()”。最初我打算使用这样的东西:

val editPreferences: SharedPreferences.Editor = Application.getSharedPreferences("preferences", Context.MODE_PRIVATE).edit()

但后来我被告知,当我真正希望它在每次调用“editPreferences”时创建一个新编辑器时,“editPreferences”将保存对同一编辑器的引用。

如果使用自定义 getter,每次都会返回一个新的编辑器吗?像这样的:

val editPreferences: SharedPreferences.Editor 
    get() = Application.getSharedPreferences("preferences", Context.MODE_PRIVATE).edit()

仍在使用 Kotlin 启动和运行,并且不确定 get() 方法是否会保留对编辑器的引用而不是创建一个新的。

【问题讨论】:

  • 简短回答:是的。

标签: android kotlin


【解决方案1】:

如果您使用自定义 getter 实现属性,它将不会存储任何数据。每次访问该属性时都会执行 getter 的主体。

【讨论】:

    【解决方案2】:

    您可以更进一步,使用委托包装属性,利用元数据上的变量名。

    用法

    class SomeActivity : SomeBaseActivity {
    
         // Declare property the with key "myImportantNumber" 
         // and default value 10
         var myImportantNumber by preference(10)
    
         //how to access the property
         fun isMyImportantNumberReallyHight() = myImportantNumber > 100
    
         //how to edit the property
         fun incrementMyImportantNumber(times:Int){
             myImportantNumber = myImportantNumber + times
         }
    }
    

    代表团

    委托保留一些首选项管理器的实例,并使用属性上的元数据来获取和保存共享首选项的值。

    class DelegatedPreference<T>(val default: T, val contextProvider:()-> Context) {
    
        val manager by lazy { PreferencesManager(contextProvider()) }
    
        @Suppress("UNCHECKED_CAST")
        operator fun getValue(thisRef: Any?, prop: KProperty<*>): T {
            return manager.get(prop.name, default)
        }
    
        operator fun setValue(thisRef: Any?, prop: KProperty<*>, value: Any) {
            manager.save(prop.name, value)
        }
    
        class TypeNotImplementedException(val propName:String) : Exception("Type of ${propName} is not implemented on DelegatedPreference and thus invalid")
    }
    

    一些糖

    一点扩展方法:

    fun <T> Activity.preference(default:T):DelegatedPreference<T>{
        return DelegatedPreference(default, {this})
    }
    

    这允许我们改变这一点:

    var myImportantNumber by DelegatedPreference(10, {this})
    

    通过更易读的东西:

    var myImportantNumber by preference(10)
    

    实际获取和保存

    这里我称之为PreferencesManager(对不起,我没有想出更好的名字)负责繁重的工作,每次需要更改属性时都会调用.edit()。它看起来像:

    public class PreferencesManager(context: Context) {
        private val preferences = getSharedPreferences(context)
    
        companion object Utils {
            public val APP_PREFERENCES: String = "APP_PREFERENCES"
    
            fun getSharedPreferences(context: Context): SharedPreferences {
                return context.getSharedPreferences(APP_PREFERENCES, Context.MODE_PRIVATE)
            }
        }
    
    
        public fun save(label:String, elem:Any){
            when(elem){
                is Int     -> preferences.edit().putInt(label, elem).apply()
                is String  -> preferences.edit().putString(label, elem).apply()
                is Float   -> preferences.edit().putFloat(label, elem).apply()
                is Boolean -> preferences.edit().putBoolean(label, elem).apply()
                else -> throw DelegatedPreference.TypeNotImplementedException(label)
            }
        }
    
        @Suppress("UNCHECKED_CAST", "IMPLICIT_CAST_TO_ANY")
        public fun <T> get(label:String, default:T):T = when(default){
            is Int     -> preferences.getInt(label, default)
            is String  -> preferences.getString(label, default)
            is Float   -> preferences.getFloat(label, default)
            is Boolean -> preferences.getBoolean(label, default)
            else -> throw DelegatedPreference.TypeNotImplementedException(label)
        } as T
    
    }
    

    这里还有很大的改进空间(比如参数化首选项名称而不是硬编码,为序列化其他类型提供扩展点等),但总体思路仍然存在。

    【讨论】:

    • 为每个问题创建一个框架并不是最实用的方法
    • 这几乎不是一个框架,只是解决平台上反复出现的问题。这是了解 Kotlin 以及我们可以通过其功能实现什么的有趣方式。
    • OP 表示他/她仍在学习 Kotlin,当我们可以利用这个机会学习新事物时,只谈论短期解决方案或快速修复是不对的!
    【解决方案3】:

    第二个属性声明适合你的需要:它有一个custom getter,因此获取属性值将始终执行getter,并且值不存储(属性没有backing field)。

    您可能对get() = ... 中的等号感到困惑,但它只是一个single-expression shorthand 用于getter 的等效形式:

    val editPreferences: SharedPreferences.Editor 
        get() { 
             return Application
                  .getSharedPreferences("preferences", Context.MODE_PRIVATE)
                  .edit()
        }
    

    【讨论】:

      【解决方案4】:

      正如hotkeyyole 提到的,自定义getter 会导致每次都返回一个新的编辑器,这正是您想要的。

      但是,请考虑使用以下超棒的解决方案:

      声明

      inline fun Context.editPreferences(preferenceFileName:String = "preferences",block:SharedPreferences.Editor.() -> Unit)
      {
          val editablePreferences = getSharedPreferences(preferenceFileName,Context.MODE_PRIVATE).edit()
          editablePreferences.block()
          editablePreferences.commit()
      }
      

      用法

      Application.editPreferences()
      {
          putBoolean("SOME_BOOLEAN",true)
          putFloat("SOME_FLOAT",293232F)
      }
      

      或者在很多情况下,当接收者已经是 Context 时,您可以这样做:

      editPreferences()
      {
          putBoolean("SOME_BOOLEAN",true)
          putFloat("SOME_FLOAT",293232F)
      }
      

      科特林❤

      【讨论】:

      • 另外,使用带有接收器的函数作为editPreferences 的参数在此处会更像DSL(输入SharedPreferences.Editor.() -&gt; Unit)。
      • 请将函数设为inline 以减少开销。
      猜你喜欢
      • 2021-08-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-31
      • 2019-06-29
      • 1970-01-01
      • 2014-07-24
      • 1970-01-01
      • 2012-04-02
      相关资源
      最近更新 更多