委托属性算是 Kotlin 语言中的高级特性,初次接触可能毫无头绪,再次接触还是一脸懵逼。只有在深入理解其语言特性和实现原理之后,才能对这一甜之又甜的“语法糖”有所认识,从而极大提高代码效率。在笔者的项目 UESTC_BBS 中,对自带 SharedPreferences 的封装 PreferenceUtils 就使用了委托属性,故一直想找机会写一篇笔记类型的文章将其记录下来。委托属性的基础是委托,一种设计模式,操作的对象不用自己执行,而是委托给另一个辅助对象。
委托
基本使用
定义一个委托属性的基本语法为 val/var <属性名>: <类型> by <表达式>表达式>类型>属性名>
,在 by 后面的表达式即为委托, 属性对应的 get()
与set()
会被委托给它的 getValue()
与 setValue()
方法。
class Example { |
上述代码中,Delegate 内方法的参数 property 原本为 KProperty<*> 接口类型,为了手动调用其方法,改成 Any 以实现传参。*>
控制台输出结果为
Example@1175e2db, thank you for delegating 'p' to me! |
可以看到属性 p 委托给了 Delegate() 对象实例,按照约定,该对象必须声明具有getValue
、setValue
方法,且方法参数个数必须大于2、3。
为了更清楚的了解这一过程,可以将代码改写成另一种形式
class Example { |
当我们使用 p 时调用其get
方法,给 p 赋值时调用其set
方法,并且都是通过委托对象 delegate 实现。
标准委托
lazy
函数lazy
接收一个 lambda 表达式并返回一个 Lazy
实例,默认情况下其线程安全
//方法签名 |
只有在第一次调用 s 时才会执行传递给 lazy()
的 lambda 表达式并返回一个记录下来的结果, 后续调用 get()
只会返回记录的结果。底层原理在于函数签名中的SynchronizedLazyImpl
方法,其中会检查变量的值,判断其是否为默认值,如果是则执行初始化函数,否则直接返回结果,具体代码可以查阅LazyJVM.kt
文件。
控制台输出为
get |
observable
字面意思,用委托的方式来定义一个可观察属性。该函数的方法签名为
public inline fun |
其接收两个参数,第一个为默认值,第二个为 lambda 表达式,位于Delegates.kt
,具体使用
var name: String by Delegates.observable("initialValue") { |
控制台输出
initialValue -> newValue0 |
Storing
将对象的属性委托值 map 中,用于解析 JSON 或者其他动态工作,不过应用较少
val user = User(mapOf("name" to "Jack", "age" to 20)) |
典型应用
封装一个 SharedPreferences(简称 SP) 是 Android 开发中经常要做的事,因为直接调用 SP 足够繁琐。如果是 Java 代码,则代码基本如下
public final class PreferencesUtil { |
外部调用
if (PreferencesUtil.getInstance().getBoolean(Constant.IS_FIRST_LAUNCH, Constant.DEF_IS_FIRST_LAUNCH)) { |
使用 Kotlin 的委托属性之后实现就简洁很多
class PreferenceUtils<T>(val context: Context, val name: String, val default: T): ReadWriteProperty |
其中接口ReadWriteProperty
为系统提供的规范接口,其中定义了getValue/setValue
方法。外部调用如下
var themeCode by PreferenceUtils(context, Constant.theme_code, default = 1) |