Kotlin学习快速入门(8)—— 委托
原文地址:Kotlin学习快速入门(8)—— 属性委托 - Stars-One的杂货小窝
委托其实是一种设计模式,但Kotlin把此特性编写进了语法中,可以方便开发者快速使用
委托对应的关键字是by
属性委托
先讲下属性委托吧,首先,复习下kotlin中设置set和get方法
默认的set和get我们可以隐藏,实际上一个简单的类代码如下:
class Person {
var personName = ""
// 这是默认的 get/set(默认是隐藏的)
get() = field
set(value) {
field = value
}
}
这里具体知识点可以查看之前所说Kotlin学习快速入门(3)——类 继承 接口 - Stars-One的杂货小窝
当然,如果是数据bean类,我们会将get和set方法隐藏(或者使用data
关键字来声明一个数据类)
若我们需要在get或set方法的时候做一下逻辑处理,比如说上面的personName
字段,我们只允许接收长度小于等于10的字符串,超过10长度的字符串就不接收(即不设置新数值),则是应该这样写:
class Person{
var personName = ""
// 这是重写的 get/set
get() = "PersonName $field"
set(value) {
field = if (value.length <= 10) value else field
}
}
然后,我们再延伸出来,如果此规则不止应用于personName字段,还可用到其他类的字段中,这个时候就是使用到属性委托。
简单描述: 我们将此规则抽取出来,需要应用到此规则的字段的get/set方法委托给规则去做,这就叫属性委托
延迟加载(懒加载)
在开始讲属性委托之前,先说明下延迟加载
Kotlin中提供了lazy方法,使用by
+lazy{}
联用,我们就实现延迟加载(也可称作懒加载)
fun main() {
val demo = Demo()
val textContent = demo.textContent
val result = demo.textContent.substring(1)
println(result)
println("打印:$textContent")
}
class Demo{
val textContent by lazy { loadFile() }
}
fun loadFile(): String {
println("读取文件...")
//模拟读取文件返回数据
return "读取的数据"
}
这里的关键词by出现在属性名后面,表示属性委托,即将属性的读和写委托给另一个对象,被委托的对象必须满足一定的条件:
- 对于
val
修饰的只读变量进行属性委托时,被委托的对象必须实现getValue()
接口,即定义如何获取变量值。 - 对于
var
修饰的读写变量进行属性委托时,被委托对象必须实现getValue()
和setValue()
接口,即定义如何读写变量值。
lazy()
方法,接收一个lambda函数,返回值是一个Lazy对象,所以就可以简写成上面的样子,其只实现了getValue()
接口,所以,当你尝试将textContent
改为var类型,IDE会提示报错!!
也是因为这点,属于延迟加载的字段,是不可被再次修改了,所以采用lazy懒加载的方式,其实就是单例模式
Delegates.vetoable
还记得上述我们要实现的规则吗,其实Kotlin中已经有了几个默认的委托规则供我们快速使用(上述的lazy其实也是一个)
Delegates.vetoable()的规则就是上述规则的通用封装,解释为:
但会在属性被赋新值生效之前会传递给Delegates.vetoable()
进行处理,依据Delegates.vetoable()
的返回的布尔值判断要不要赋新值。
如下面例子:
class Person {
var personName by Delegates.vetoable("") { property, oldValue, newValue ->
//当设置的新值满足条件,则会设置为新值
newValue.length <= 10
}
}
Delegates.notNull
设置字段不能为null,不过想不到具体的应用情景
class Person {
var personName by Delegates.notNull<String>()
}
Delegates.observable
使用Delegates.observable
可以帮我们快速实现观察者模式,只要字段数值发生改变,就会触发
class Person{
var age by Delegates.observable(0){ property, oldValue, newValue ->
//这里可以写相关的逻辑
if (newValue >= 18) {
tip = "已成年"
}else{
tip = "未成年"
}
}
var tip =""
}
上面的例子就比较简单,设置age同时更新提示,用来判断是否成年
val person = Person()
person.age = 17
println(person.tip)
补充-自定义委托
上述都是官方定义好的一些情形,但如果不满足我们的需求,这就需要自定义委托了
官方提供了两个基础类供我们自定义委托使用:
ReadWriteProperty
包含get和set方法,对应var
关键字
ReadOnlyProperty
只有get方法,对应val
关键字
PS:实际上,我们自己随意创建个委托类也是可以的,不过这样写不太规范,所以我们一般直接实现官方给的上述两个类即可
ReadWriteProperty和ReadOnlyProperty都需要传两个泛型,分别为R,T
R
持有属性的类型T
字段类型
可能上面描述不太明白,下面给个简单例子,Person类中有个name字段(String),首字母需要大写:
class Person {
var name by NameToUpperCase("")
}
class NameToUpperCase(var value:String) :ReadWriteProperty<Person,String>{
//NameToUpperCase类中默认有个属性字段,用来存数据
override fun getValue(thisRef: Person, property: KProperty<*>): String {
return this.value
}
override fun setValue(thisRef: Person, property: KProperty<*>, value: String) {
//在设置数值的时候,将第一个字母转为大写,一般推荐在setValue里编写逻辑
this.value = value.substring(0,1).toUpperCase()+value.substring(1)
}
}
个人看法,一般在setValue的时候进行设置数值比较好,因为getValue作操作的话,会触发多次,处理的逻辑复杂的话可能会浪费性能...
当然,再提醒下,上面的逻辑也可以直接去字段里的setValue()
里面改,委托只是方便抽取出去供其他类应用同样的规则
PS: 如果你的委托不是针对特定的类,R泛型可以改为Any
类委托
这个一般与多态一起使用,不过个人想不到有什么具体的应用情景,暂时做下简单的记录
interface IDataStorage{
fun add()
fun del()
fun query()
}
class SqliteDataStorage :IDataStorage{
override fun add() {
println("SqliteDataStorage add")
}
override fun del() {
println("SqliteDataStorage del")
}
override fun query() {
println("SqliteDataStorage query")
}
}
假如现在我们有个MyDb类,查询的方法与SqliteDataStorage这个里的方法有所区别,但其他方法都是没有区别,这个时候就会用到类委托了
有以下几种委托的使用方式
1.委托类作为构造器形参传入(常用)
class MyDb(private val storage:IDataStorage) : IDataStorage by storage{
override fun add() {
println("mydb add")
}
}
val db = MyDb(SqliteDataStorage())
db.add()
db.query()
输出结果:
mydb add
SqliteDataStorage query
如果是全部都是委托给SqliteDataStorage的话,可以简写为这样:
class MyDb(private val storage:IDataStorage) : IDataStorage by storage
2.新建委托类对象
class MyDb : IDataStorage by SpDataStorage(){
override fun add() {
println("mydb add")
}
}
这里测试的效果与上文一样,不在重复赘述
参考
- kotlin-属性委托_南郭竽的博客-CSDN博客_kotlin属性委托
- Kotlin 基础 | 委托及其应用_普通网友的博客-CSDN博客
- Kotlin修炼指南(五)—Delegates_eclipse_xu的博客-CSDN博客
- kotlin类委托、属性委托_水煮鱼在飞的博客-CSDN博客_kotlin 类委托
Kotlin学习快速入门(8)—— 委托的更多相关文章
- Kotlin学习快速入门(7)——扩展的妙用
原文地址: Kotlin学习快速入门(7)--扩展的妙用 - Stars-One的杂货小窝 之前也模模糊糊地在用这个功能,也是十分方便,可以不用继承,快速给某个类增加新的方法,本篇便是来讲解下Kotl ...
- Kotlin学习快速入门(3)——类 继承 接口
类 参考链接 类定义格式 使用class关键字定义,格式如下: class T{ //属性 //构造函数 //函数 //内部类 } Java Bean类 java bean类 //java bean类 ...
- Kotlin学习快速入门(4)——集合使用
List,Set,Map都是集合 List 是一个有序集合,可通过索引(反映元素位置的整数)访问元素.元素可以在 list 中出现多次.列表的一个示例是一句话:有一组字.这些字的顺序很重要并且字可以重 ...
- Kotlin学习快速入门(1)——基本数据类型以及String常用方法使用
本文适合有Java基础的人 Kotlin语法特点 相比java,省略括号,可以自动判断类型,省略new关键字,空指针捕获 主函数 kotlin文件(kt文件)中,只有要下列的方法,就可以运行,无需像之 ...
- Kotlin学习快速入门(2)——条件 数组 循环 方法
条件 if条件判断 常用的判断和Java一样,这里提一下不同的用法 1.if可以作为三元运算符 val max = if (a > b) a else b 2.使用in判断是否在某个区间 val ...
- Kotlin学习快速入门(5)——空安全
介绍 kotlin中,对象可分为两种类型,可为空的对象和不可为空对象 默认为不可为空对象,代码检测如果发现不可为空对象赋予了null,则会标红报错. 可为空的对象,如果调用了方法,代码检测也会标红报错 ...
- pytest学习--快速入门
一.pytest简介 Pytest是python的一种单元测试框架. pytest的特点: 入门简单,文档丰富 支持单元测试,功能测试 支持参数化,重复执行,部分执行,测试跳过 兼容其他测试框架(no ...
- pytorch入门--土堆深度学习快速入门教程
工具函数 dir函数,让我们直到工具箱,以及工具箱中的分隔区有什么东西 help函数,让我们直到每个工具是如何使用的,工具的使用方法 示例:在pycharm的console环境,输入 import t ...
- Html5 学习系列(五)Canvas绘图API快速入门(1)
引言:Canvas绘图API快速入门 在接触HTML5的初学者包括我都在很多地方见到非常炫的一些页面,甚至好多学习HTML5的开发者都是冲着Web端的页游去的,那么HTML5那么绚丽的页面效果以及游戏 ...
- Gradle学习系列之一——Gradle快速入门
这是一个关于Gradle的学习系列,其中包含以下文章: Gradle快速入门 创建Task的多种方法 读懂Gradle语法 增量式构建 自定义Property 使用java Plugin 依赖管理 构 ...
随机推荐
- Protobuf之proto基础语法
目录 1.说明 2.字段类型 3.字段规则 4.字段编号 5.注释 6.类型 6.1.message 6.2.service 7.枚举enum 8.保留字段 9.import 9.1.protoc指令 ...
- 5、后端学习规划:.Net学习 - 学习规划系列文章
.Net是微软发布的一整套的软件编程解决方案.笔者从大学的时代开始就阅读.netframework的书籍了,但是当时没有进行实践.毕业后,笔者去了微软技术中心的公司上班,所以就接触了.net以及C#编 ...
- 错误: tensorflow.python.framework.errors_impl.OutOfRangeError的解决方案
近日,在使用CascadeRCNN完成目标检测任务时,我在使用这个模型训练自己的数据集时出现了如下错误: tensorflow.python.framework.errors_impl.OutOfRa ...
- Idhttp Post https 连接 报“IOHandler value is not valid.”错误
今天研究阿里巴巴的对接,发现IDHTTP 的post 如果是 https 的连接就会报:"IOHandler value is not valid."错误 加载https的站点页面 ...
- .NET Core开发实战(第15课:选项框架:服务组件集成配置的最佳实践)--学习笔记
15 | 选项框架:服务组件集成配置的最佳实践 这一节讲解如何使用选项框架来处理服务和配置的关系 选项框架的特性: 1.支持单例模式读取配置 2.支持快照 3.支持配置变更通知 4.支持运行时动态修改 ...
- 静态RMQ处理方式合辑
这里汇集了所有我知道的静态区间最大值做法. \(O(n)\) 预处理,\(O(n)\) 回答. 每一次询问暴力处理即可. \(O(n^2)\) 预处理,\(O(1)\) 回答. 预处理出所有的答案. ...
- P2P通讯方式
概述 实现p2p通讯我们提供两种方式,这两种方式分别是通过客户端直接互通和p2p映射: 无论哪一种,首先设备两端都得部署好fastnat客户端,NAT类型不能是对称类型NAT(Symmetric),否 ...
- Windows xp 无法远程连接Windows Server 2008
-------------------------转载自: Windows XPSP3通过网络级身份验证方式连接Windows Server 2008远程桌面 远程桌面大大方便了大家的日常管理工作,W ...
- 《系列二》-- 3、FactoryBean 的使用
目录 FactoryBean 解决的问题 FactoryBean 接口初识 改造结果 最后的补充 回顾下 FactoryBean 的应用 factory-method 和 factory-bean 的 ...
- error C2039: "function": 不是 "std" 的成员的解决方法
这个错误通过某度没找到合适的解决方案,故记录下来 其实如果使用 google 搜索错误的英文关键词,大概第一条就是解决问题的链接 Large number of "'function' is ...