写点什么

kotlin 实战之委托总结,成为一名合格 Android 架构师

用户头像
Android架构
关注
发布于: 11 小时前

4: iload_1


5: invokeinterface #29, 2 // InterfaceMethod cn/yan/test/InterfaceBase.print:(I)V


10: return


}


[](


)kotlin 属性委托




对于 kotlin 的属性委托来说,我们有如下要求:


  • 对于只读属性来说(val 修饰的属性),委托需要提供一个名为 getValue 的方法,该方法需要提供的参数如下:

  • thisRef:需要是属性拥有者相同的类型或者是其父类型(对于扩展属性来说,这个类型指的是被扩展的那个类型)。

  • property:需要是KProperty<*>类型或者是其父类型。


getValue 方法需要返回与属性相同的类型或者其子类型。


  • 对于可变读写属性(var 修饰的属性),委托需要提供只读属性的 getValue 方法外,还需要提供一个名为 setValue 的方法,该方法需要提供的参数如下:

  • thisRef:需要是属性拥有者相同的类型或者是其父类型(对于扩展属性来说,这个类型指的是被扩展的那个类型)。

  • property:需要是KProperty<*>类型或者是其父类型。

  • value:需要与属性的类型相同或是其父类型。

  • getValue、setValue 方法既可以作为委托类的成员方法实现,也可以作为其扩展方法来实现。

  • getValue、setValue 方法都必须要标记为 operator 关键字。对于委托类来说,它可以实现 ReadOnlyProperty 或是 ReadWriteProperty 接口,这些接口包含了相应的 getValue、setValue 方法。对于委托类来说,它也可以不实现上面两个接口,而单独提供符合约定的 getValue、setValue 方法,其效果是一样的。


我们看一个属性委托样例:


//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober


//属性委托方法定义有严格的格式要求


//两个方法的定义签名必须按照要求来,不能修改


class PropertyDelete {


operator fun getValue(thisRef: Any?, property: KProperty<*>): String {


return "{property.name}"


}


operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {


println("value")


}


}


class PropertyClass {


//通过属性委托,不用给 name 赋值,因为其 set 和 get 方法都被委托到了 PropertyDelete 对象


var name: String by PropertyDelete()


}


/**


调用结果


cn.yan.test.PropertyClass@30946e09, new value is 7890


cn.yan.test.PropertyClass@30946e09, your deleted property name is name


*/


fun testRun() {


val test = PropertyClass()


test.name = "7890"


println(test.name)


}


属性委托有四种情况在实际开发中比较常用:


  • 延迟属性。

  • 非空属性。

  • 可观测属性。

  • map 委托。


延迟属性: 属性只有在第一次访问时才会计算,之后则会将之前的计算结果缓存起来供后续调用。如下是一个案例:


//延迟属性:依赖 kotlin 提供的 lazy 函数实现,函数参数是一个 lambada 表达式


//源码 LazyJVM.kt 中 public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer) 方法


val lazyValue: Int by lazy {


println("lazyValue lazy")


28


}


/**


调用结果


lazyValue lazy


28


28


*/


fun testRun() {


//首次调用时触发计算


println(lazyValue)


//可以看到后续调用是直接用了上次的缓存结果


println(lazyValue)


}


非空属性: 适用于那些无法在初始化阶段确认属性值的场合。lateinit 修饰符只能在类(不在主构造函数中)内声明的 var 属性上使用,而且只有在该属性没有自定义集合或者设置器时,此外属性的类型必须是非空的,并且不能是基元类型。而非空属性没有这些限制。其他他们的作用是相同的。如下是一个案例:


//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober


class Tree {


//非空属性解决了 var name: String? = null 导致后续判断冗余


//非空属性解决了 var name: String = "" 初值隐晦问题


//非空属性解决了 lateinit 的一些缺陷,譬如 lateinit 只能应用于非基元类型,譬如不能用于 Int 等问题


var name: String by Delegates.notNull<String>()


}


/**


调用


*/


fun testRun() {


val tree = Tree()


//运行时异常,没有赋值而使用 IllegalStateException: Property name should be initialized before get.


//println(tree.name)


tree.name = "123"


println(tree.name)


}


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


可观测属性: kotlin 提供了 observable 赋值后观测器和 vetoable 赋值前拦截观测器的能力。如下是一个案例:


class Tree {


//可观测属性初值,10 是属性初值,当属性被赋值后会触发回调 lambada 表达式


var age: Int by Delegates.observable(10) {


property, oldValue, newValue ->


println("property name is oldValue, new value is $newValue")


}


//可观测属性初值,10 是属性初值,当属性被赋值前会触发回调 lambada 表达式,可以做到类似属性值赋值拦截器的效果


//这里当赋值小于等于 0 则丢弃


var defaultCount: Int by Delegates.vetoable(10) {


property, oldValue, newValue -> when {


newValue <= 0 -> {


println("unsupport value of $newValue, refused!")


return@vetoable false


}


else -> {


println("property name is oldValue, new value is $newValue")


return@vetoable true


}


}


}


}


/**


调用结果


property name is age, old value is 10, new value is 10


property name is age, old value is 10, new value is 11


property name is age, old value is 11, new value is 12


12




property name is defaultCount, old value is 10, new value is 100


unsupport value of -2, refused!


property name is defaultCount, old value is 100, new value is 101


101


*/


fun testRun() {


val tree = Tree()


tree.age = 10


tree.age = 11


tree.age = 12


println(tree.age)


println("-------------")


tree.defaultCount = 100


tree.defaultCount = -2


tree.defaultCount = 101


println(tree.defaultCount)


}


map 委托: 可以将属性值存储到 map 当中。通常出现在 json 解析或者一些动态行为,在这种情况中可以将 map 实例作为类中属性的委托。注意:map 中的 key 名字必须要和属性的名字一致才行,否则委托后运行解析时会抛出 NoSuchElementException 异常提示 key name 不存在。下面是只读 map 属性委托的案例:


class Result (map: Map<String, Any?>) {


val name: String by map


val address: String by map


val age: Int by map


val date: Date by map


override fun toString(): String {


return "{name: {this.address}, " +


"age: {this.date}}"


}


}


/**


调用结果


{name: ruoshui, address: zhuhai, age: 18, date: Sun Oct 18 14:50:21 CST 2019}


*/


fun testRun() {


val result = Result(mapOf(


"name" to "ruoshui",


"address" to "zhuhai",


"age" to 18,


"date" to Date()


))


println(result.toString())


}


下面是读写 map 属性委托的案例(可以看到背后都是交给 map 存储的,写属性后 map 会跟着变化):


//【工匠若水 加微信 yanbo373131686 联系我,关注微信公众号:码农每日一题 未经允许严禁转载 https://blog.csdn.net/yanbober


class Result (map: MutableMap<String, Any?>) {


var name: String by map


var address: String by map


var age: Int by map


var date: Date by map


override fun toString(): String {


return "{name: {this.address}, " +


"age: {this.date}}"


}


}


/**


调用结果


{name: ruoshui, address: zhuhai, age: 18, date: Sun Oct 18 14:57:25 CST 2020}


ruoshui




{name: gongjiang, address: zhuhai, age: 18, date: Sun Oct 18 14:57:25 CST 2020}


gongjiang


*/


fun testRun() {


val map: MutableMap<String, Any?> = mutableMapOf(


"name" to "ruoshui",


"address" to "zhuhai",


"age" to 18,


"date" to Date()


)


val result = Result(map)


println(result.toString())


println(map["name"])


println("--------")

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
kotlin 实战之委托总结,成为一名合格Android架构师