【Kotlin -- 知识点】Kotlin 中的委托
文章目录
- 一、什么是委托?
- 二、使用场景
- 三、属性委托
- 四、Kotlin 标准库中提供几个委托
一、什么是委托?
委托,也就是委托模式,它是23种经典设计模式种的一种,又名代理模式,在委托模式中,有2个对象参与同一个请求的处理,接受请求的对象将请求委托给另一个对象来处理。委托模式是一项技巧,其他的几种设计模式如:策略模式、状态模式和访问者模式都是委托模式的具体场景应用。
委托模式中,有三个角色,约束、委托对象和被委托对象。
解释说明:
约束: 约束是接口或者抽象类,它定义了通用的业务类型,也就是需要被代理的业务
被委托对象: 具体的业务逻辑执行者
委托对象: 负责对真是角色的应用,将约束累定义的业务委托给具体的委托对象。
二、使用场景
现在很多年轻人都爱完游戏,不管是吃鸡、王者荣耀还是英雄联盟。它们都是有等级之分的:青铜->白银->黄金->铂金->钻石->宗师->王者,等级越高,代表你越厉害,就拿英雄联盟来说,我们多数混迹在白银黄金阶段,要上钻石宗师段位非常困难。比如你排位打了很久,就差几场就能上宗师了,老是打不上去,这个时候怎么办呢?好办,现在有很多游戏代练,委托游戏代练给你打上去就好了。这其实就是一个委托模式。
首先,我们定义约束类,定义我们需要委托的业务,就拿这个场景来说,我们的业务就是打排位赛,升级。因此,定义个约束类(接口)IGamePlayer:
// 约束类
interface IGamePlayer {// 打排位赛fun rank()// 升级fun upgrade()
}
约束类中,定义了我们要代理的业务rank(),upgrade(),然后,我们就定义被委托对象,也就是游戏代练:
// 被委托对象,本场景中的游戏代练
class RealGamePlayer(private val name: String): IGamePlayer{override fun rank() {println("$name 开始排位赛")}override fun upgrade() {println("$name 升级了")}}
如上,我们定义了一个被委托对象RealGamePlayer, 它有一个属性name,它实现了我们约定的业务(实现了接口方法)。
接下来,就是委托角色:
// 委托对象
class DelegateGamePlayer(private val player: IGamePlayer): IGamePlayer by player
我们定义了一个委托类 DelegateGamePlayer
, 现在游戏代练有很多,水平有高有低,如果发现水平不行,我们可以随时换,因此,我们把被委托对象作为委托对象的属性,通过构造方法传进去。
注意:在 Kotlin 中,委托用关键字 by 修饰,by 后面就是你委托的对象,可以是一个表达式。因此在本例中,通过 by player
委托给了具体的被委托对象。
测试类:
// Client 场景测试
fun main() {val realGamePlayer = RealGamePlayer("张三")val delegateGamePlayer = DelegateGamePlayer(realGamePlayer)delegateGamePlayer.rank()delegateGamePlayer.upgrade()
}
运行结果:
张三 开始排位赛
张三 升级了
三、属性委托
在Kotlin 中,有一些常见的属性类型,虽然我们可以在每次需要的时候手动实现它们,但是很麻烦,各种样板代码存在,我们知道,Kotlin可是宣称要实现零样板代码的。为了解决这些问题呢?Kotlin标准为我们提供了委托属性。
class Test {// 属性委托var prop: String by Delegate()
}
委托属性的语法如下:
val/var <属性名>: <类型> by <表达式>
- 属性委托的原理
class Delegate {operator fun getValue(thisRef: Any?, property: KProperty<*>): String {return "$thisRef, thank you for delegating '${property.name}' to me!"}operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {println("$value has been assigned to '${property.name}' in $thisRef.")}
}
其中的参数解释如下:
thisRef
—— 必须与 属性所有者 类型(对于扩展属性——指被扩展的类型)相同或者是它的超类型;property
—— 必须是类型 KProperty<*>或其超类型。value
—— 必须与属性同类型或者是它的子类型。
测试:
fun main() {println(Test().prop)Test().prop = "Hello, Android 技术!"
}
输出结果:
Test@5197848c, thank you for delegating ‘prop’ to me! Hello,
Android技术杂货铺! has been assigned to ‘prop’ in Test@17f052a3.
- 另一种实现属性委托的方式
Kotlin 标准库中声明了2个含所需 operator方法的ReadOnlyProperty / ReadWriteProperty
接口。
interface ReadOnlyProperty<in R, out T> {operator fun getValue(thisRef: R, property: KProperty<*>): T
}interface ReadWriteProperty<in R, T> {operator fun getValue(thisRef: R, property: KProperty<*>): Toperator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
被委托类 实现这两个接口其中之一就可以了,val 属性实现 ReadOnlyProperty
,var 属性实现ReadOnlyProperty
。
// val 属性委托实现
class Delegate1: ReadOnlyProperty<Any,String>{override fun getValue(thisRef: Any, property: KProperty<*>): String {return "通过实现ReadOnlyProperty实现,name:${property.name}"}
}
// var 属性委托实现
class Delegate2: ReadWriteProperty<Any,Int>{override fun getValue(thisRef: Any, property: KProperty<*>): Int {return 20}override fun setValue(thisRef: Any, property: KProperty<*>, value: Int) {println("委托属性为: ${property.name} 委托值为: $value")}}
// 测试
class Test {// 属性委托val d1: String by Delegate1()var d2: Int by Delegate2()
}
测试代码如下:
val test = Test()println(test.d1)println(test.d2)test.d2 = 100
输出结果:
通过实现ReadOnlyProperty实现,name:d1
20
委托属性为: d2 委托值为: 100
四、Kotlin 标准库中提供几个委托
Kotlin 标准库中提供了几种委托,例如:
- 延迟属性(lazy properties): 其值只在首次访问时计算;
- 可观察属性(observable properties): 监听器会收到有关此属性变更的通知;
- 把多个属性储存在一个映射(map)中,而不是每个存在单独的字段中。
4.1 延迟属性 lazy
lazy() 是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。
val lazyProp: String by lazy {println("Hello,第一次调用才会执行我!")"西哥!"
}// 打印lazyProp 3次,查看结果
fun main() {println(lazyProp)println(lazyProp)println(lazyProp)
}
输出结果:
Hello,第一次调用才会执行我!
西哥!
西哥!
西哥!
4.2 可观察属性 Observable
如果你要观察一个属性的变化过程,那么可以将属性委托给Delegates.observable, observable
函数原型如下:
public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):ReadWriteProperty<Any?, T> =object : ObservableProperty<T>(initialValue) {override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)}
参数:
initialValue
: 初始值onChange
: 属性值被修改时的回调处理器,回调有三个参数property,oldValue,newValue
,分别为: 被赋值的属性、旧值与新值。
使用:
var observableProp: String by Delegates.observable("默认值:xxx"){property, oldValue, newValue ->println("property: $property: $oldValue -> $newValue ")
}
// 测试
fun main() {observableProp = "第一次修改值"observableProp = "第二次修改值"
}
输出结果:
property: var observableProp: kotlin.String: 默认值:xxx -> 第一次修改值
property: var observableProp: kotlin.String: 第一次修改值 -> 第二次修改值
4.3 属性存储在映射中
还有一种情况,在一个映射(map)里存储属性的值,使用映射实例自身作为委托来实现委托属性,如:
class User(val map: Map<String, Any?>) {val name: String by mapval age: Int by map
}
测试如下:
fun main() {val user = User(mapOf("name" to "西哥","age" to 25))println("name=${user.name} age=${user.age}")
}
输出结果:
name=西哥 age=25
使用映射实例自身作为委托来实现委托属性,可以使用在 json 解析中,因为 json 本身就可以解析成一个 map 。
【Kotlin -- 知识点】Kotlin 中的委托相关推荐
- 一文彻底搞懂 Kotlin 中的委托 | 开发者说·DTalk
本文原作者: 码农西哥,原文发布于微信公众号: 技术最TOP https://mp.weixin.qq.com/s/BD1zT80IADDZS4CAxmooPg 什么是委托? 委托,也就是委托模式, ...
- 在kotlin companion object中读取Bean,注入Bean对象
在kotlin companion object中读取Bean,注入Bean对象 在使用kotlin时,或多或少地会使用到一些公共组件,如 http. mongo. redis相关的组件. 使用组 ...
- 在kotlin companion object中读取spring boot配置文件,静态类使用@Value注解配置
在kotlin companion object中读取配置文件 静态类使用@Value注解配置 class Config {@Value("\${name}")fun setNam ...
- 【Kotlin】Kotlin 语言集合中的高阶函数详解 ( 数据类 data class | maxBy | minBy | filter | map | any | count | find )
文章目录 I . List 集合高阶函数引入 II . Kotlin 数据类 ( data class ) III . Java 代码 与 Kotlin 代码实现对比 ( 查询年龄最大的 ) IV . ...
- kotlin能用嵌入式linux,Kotlin在项目中的应用和踩过的坑
应用 空类型安全 Kotlin引入了可空类型(用?标识),在编译期杜绝了可空类型直接调用方法的可能. var a: String = "abc" a = null // 编译错误 ...
- kotlin数据库_如何在Kotlin应用程序中使用Xodus数据库
kotlin数据库 I want to show you how to use one of my favorite database choices for Kotlin applications. ...
- kotlin调用类中的方法_一种轻松的方法来测试Kotlin中令人沮丧的静态方法调用
kotlin调用类中的方法 by Oleksii Fedorov 通过Oleksii Fedorov 一种轻松的方法来测试Kotlin中令人沮丧的静态方法调用 (A stress-free way t ...
- Android Gson在Kotlin data class中的使用
文章目录 Android Gson在Kotlin data class中的使用 基本使用 NEP 空指针异常问题 空指针异常产生的原因 空安全失效问题 字段全有默认值 字段部分有默认值 解决问题 使用 ...
- Kotlin在forEach中如何跳出循环和跳出当前循环体
一.数组的forEach中直接retrun fun main(args: Array<String>) {val arr = intArrayOf(1,2,3,4,5,6,7)arr.fo ...
- 【Kotlin】Kotlin 教程
kotlin 教程 前言 什么是kotlin 什么是Java? kotlin 与 Java有什么区别 kotlin 的特点 Java的特点 kotlin 的历史 JAVA的历史 kotlin 的优势 ...
最新文章
- 博弈论笔记1:囚徒困境与纳什均衡
- 搭建 springMVC 框架
- mysqlbinlog工具_mysqlbinlog命令详解 Part 1-实验环境准备
- Linux中find用法
- mysql 值到99999后不增值了_MySQL必知必会3
- java 递归 遍历目录下的所有文件
- 《VMware虚拟机实用宝典》繁体中文版封面
- 函数的嵌套,名称空间和作用域
- (一) 双目立体视觉介绍
- 2.aop原理:@EnableAspectJAutoProxy
- Listen1:让你畅听全网音乐,支持多平台
- web浏览器和web服务器的协议是,浏览器是如何与Web服务器进行通信的
- 【Python】所有常用Python库和功能查询表
- 反向传播算法公式推导,神经网络的推导
- 二级MS Office高级应用--Excel常用函数
- 贼法,要想打好打高,几条建议
- 文件传输工具FileZillaWinSCP
- 初识Android 制作一个简单的记账本
- C++之Queue容器初学
- Spring框架学习笔记---完结