文章目录

  • 一、什么是委托?
  • 二、使用场景
  • 三、属性委托
  • 四、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 中的委托相关推荐

  1. 一文彻底搞懂 Kotlin 中的委托 | 开发者说·DTalk

    本文原作者: 码农西哥,原文发布于微信公众号: 技术最TOP  https://mp.weixin.qq.com/s/BD1zT80IADDZS4CAxmooPg 什么是委托? 委托,也就是委托模式, ...

  2. 在kotlin companion object中读取Bean,注入Bean对象

    在kotlin companion object中读取Bean,注入Bean对象 在使用kotlin时,或多或少地会使用到一些公共组件,如 http. mongo. redis相关的组件.   使用组 ...

  3. 在kotlin companion object中读取spring boot配置文件,静态类使用@Value注解配置

    在kotlin companion object中读取配置文件 静态类使用@Value注解配置 class Config {@Value("\${name}")fun setNam ...

  4. 【Kotlin】Kotlin 语言集合中的高阶函数详解 ( 数据类 data class | maxBy | minBy | filter | map | any | count | find )

    文章目录 I . List 集合高阶函数引入 II . Kotlin 数据类 ( data class ) III . Java 代码 与 Kotlin 代码实现对比 ( 查询年龄最大的 ) IV . ...

  5. kotlin能用嵌入式linux,Kotlin在项目中的应用和踩过的坑

    应用 空类型安全 Kotlin引入了可空类型(用?标识),在编译期杜绝了可空类型直接调用方法的可能. var a: String = "abc" a = null // 编译错误 ...

  6. kotlin数据库_如何在Kotlin应用程序中使用Xodus数据库

    kotlin数据库 I want to show you how to use one of my favorite database choices for Kotlin applications. ...

  7. kotlin调用类中的方法_一种轻松的方法来测试Kotlin中令人沮丧的静态方法调用

    kotlin调用类中的方法 by Oleksii Fedorov 通过Oleksii Fedorov 一种轻松的方法来测试Kotlin中令人沮丧的静态方法调用 (A stress-free way t ...

  8. Android Gson在Kotlin data class中的使用

    文章目录 Android Gson在Kotlin data class中的使用 基本使用 NEP 空指针异常问题 空指针异常产生的原因 空安全失效问题 字段全有默认值 字段部分有默认值 解决问题 使用 ...

  9. Kotlin在forEach中如何跳出循环和跳出当前循环体

    一.数组的forEach中直接retrun fun main(args: Array<String>) {val arr = intArrayOf(1,2,3,4,5,6,7)arr.fo ...

  10. 【Kotlin】Kotlin 教程

    kotlin 教程 前言 什么是kotlin 什么是Java? kotlin 与 Java有什么区别 kotlin 的特点 Java的特点 kotlin 的历史 JAVA的历史 kotlin 的优势 ...

最新文章

  1. 博弈论笔记1:囚徒困境与纳什均衡
  2. 搭建 springMVC 框架
  3. mysqlbinlog工具_mysqlbinlog命令详解 Part 1-实验环境准备
  4. Linux中find用法
  5. mysql 值到99999后不增值了_MySQL必知必会3
  6. java 递归 遍历目录下的所有文件
  7. 《VMware虚拟机实用宝典》繁体中文版封面
  8. 函数的嵌套,名称空间和作用域
  9. (一) 双目立体视觉介绍
  10. 2.aop原理:@EnableAspectJAutoProxy
  11. Listen1:让你畅听全网音乐,支持多平台
  12. web浏览器和web服务器的协议是,浏览器是如何与Web服务器进行通信的
  13. 【Python】所有常用Python库和功能查询表
  14. 反向传播算法公式推导,神经网络的推导
  15. 二级MS Office高级应用--Excel常用函数
  16. 贼法,要想打好打高,几条建议
  17. 文件传输工具FileZillaWinSCP
  18. 初识Android 制作一个简单的记账本
  19. C++之Queue容器初学
  20. Spring框架学习笔记---完结

热门文章

  1. 华为硬件工程师手册_华为认证GaussDB OLTP数据库高级工程师正式发布
  2. 再见,深圳!再见,腾讯!
  3. 区块链(BTC)学习总结1
  4. Cabbage教学(5)——条件体
  5. linux下安装weblogic出现的两个错误解决办法
  6. java中如何插入表格_Java如何向Word文档中添加表格?
  7. 【软件质量】-01-缺陷严重等级定义
  8. Low-Light Enhancement 数据集 和 论文代码
  9. 浅谈安卓逆向协议(二)- 抖音,皮皮虾
  10. 转:__stack_chk_fail栈检查失败