kotlin 实战之委托总结

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 “$thisRef, your deleted property name is ${property.name}”

}

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

println(“$thisRef, new value is $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 lazy(initializer: () -> T): Lazy = 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()

}

/**

调用

*/

fun testRun() {

val tree = Tree()

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

//println(tree.name)

tree.name = “123”

println(tree.name)

}

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

class Tree {

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

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

property, oldValue, newValue ->

println(“property name is ${property.name}, old value 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 ${property.name}, old value 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.name}, address: ${this.address}, " +

“age: ${this.age}, date: ${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.name}, address: ${this.address}, " +

“age: ${this.age}, date: ${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(“--------”)

result.name = “gongjiang”

println(result.toString())

println(map[“name”])

}

对于每个委托属性来说,kotlin 编译器在底层会生成一个辅助的属性,然后将原有属性的访问委托给这个辅助属性。比如说,对于属性 prop 来说,kotlin 编译器所生成的隐含属性为prop$delete,然后对原有的 prop 属性的访问器的访问都只是委托给了这个额外的辅助属性。

提供委托(providing a delegate)


通过定义 provideDelegate operator,我们可以扩展委托的创建过程逻辑。如果对象定义了 provideDelegate 方法,那么该方法就会被调用来创建属性的委托实例。下面是一个案例:

//委托实现类

class PropertyDelete: ReadOnlyProperty<AnimBase, String> {

override fun getValue(thisRef: AnimBase, property: KProperty<*>): String {

return “$thisRef, your deleted property name is ${property.name}”

}

}

//提供委托实现:作用是当满足特定条件才给属性添加委托

class AnimBaseLauncher {

operator fun provideDelegate(thisRef: AnimBase, property: KProperty<*>): ReadOnlyProperty<AnimBase, String> {

println(“AnimBaseLauncher provideDelegate invoke…”)

//这里可以类似工厂依据不同条件生产不同的委托属性或者处理逻辑

when (property.name) {

“name”, “address” -> return PropertyDelete()

else -> throw Exception(“property name not valid!”)

}

}

}

//普通类

最后

感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?

Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

[外链图片转存中…(img-JDktLwhJ-1715487385970)]

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值